<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          六十九、數(shù)據(jù)結(jié)構(gòu)鏈表的實現(xiàn)

          共 4703字,需瀏覽 10分鐘

           ·

          2020-12-29 09:52


          「@Author:Runsen」

          編程的本質(zhì)來源于算法,而算法的本質(zhì)來源于數(shù)學(xué),編程只不過將數(shù)學(xué)題進(jìn)行代碼化。「---- Runsen」

          鏈表 Linked List

          鏈表由許多結(jié)點(也可以叫節(jié)點或元素)組成,每一個結(jié)點有兩個域,左邊部份叫值域 data,用于存放用戶數(shù)據(jù);右邊叫指針域 next,一般是存儲著到下一個元素的指針。head結(jié)點(頭節(jié)點),head是一個特殊的結(jié)節(jié),head結(jié)點永遠(yuǎn)指向第一個結(jié)點,tail結(jié)點(尾節(jié)點),tail結(jié)點也是一個特殊的結(jié)點,tail結(jié)點永遠(yuǎn)指向最后一個節(jié)點,tail.next = None。

          節(jié)點結(jié)構(gòu)

          上面四個節(jié)點 abcd 組成一個鏈表,每一個節(jié)點都包含 data 和 next,尾節(jié)點的 next 指向 None。

          鏈表中的元素都會兩個屬性,一個是元素的值,另一個是指針,此指針標(biāo)記了下一個元素的地址,每一個數(shù)據(jù)都會保存下一個數(shù)據(jù)的內(nèi)存的地址,通過此地址可以找到下一個數(shù)據(jù),任意位置插入元素和刪除元素效率較高,時間復(fù)雜度為O(1)。

          要需要訪問某個位置的數(shù)據(jù),需要從第一個數(shù)據(jù)開始找起,依次往后遍歷,直到找到待查詢的位置,故可能在查找某個元素時,時間復(fù)雜度達(dá)到O(N)

          優(yōu)點:

          • 任意位置插入元素和刪除元素的速度快,時間復(fù)雜度為O(1)
          • 內(nèi)存利用率高,不會浪費內(nèi)存

          缺點:

          • 隨機(jī)訪問效率低,時間復(fù)雜度為0(N)

          鏈表與我們熟悉的數(shù)組相對比,數(shù)組需要一塊連續(xù)的內(nèi)存空間來存儲,一經(jīng)聲明就要占用整塊連續(xù)內(nèi)存空間,對內(nèi)存的要求比較高,如果聲明的數(shù)組過大,系統(tǒng)可能沒有足夠的連續(xù)空間分配給它。

          而鏈表恰恰相反,它并不需要一塊連續(xù)的內(nèi)存空間,它通過“指針”將一組零散的內(nèi)存塊串聯(lián)起來使用。

          雙鏈表 Doubly Linked List

          雙向鏈表也叫雙鏈表,是鏈表的一種,它的每個數(shù)據(jù)結(jié)點中都有兩個指針,分別指向直接后繼和直接前驅(qū)。所以,從雙向鏈表中的任意一個結(jié)點開始,都可以很方便地訪問它的前驅(qū)結(jié)點和后繼結(jié)點。

          鏈表代碼實現(xiàn)

          下面以class類創(chuàng)建節(jié)點, 每個節(jié)點包含當(dāng)前節(jié)點所要存的數(shù)據(jù)data,和指向下一節(jié)點的指針。

          節(jié)點類的定義很簡單,節(jié)點只有兩個屬性,data 和 next,next指向下一個節(jié)點。定義完節(jié)點類后,可以創(chuàng)建節(jié)點,但是還需要將節(jié)點連接在一起,構(gòu)成鏈表才可以。

          我們用 Python 語言來自己實現(xiàn)一個單向鏈表結(jié)構(gòu),以加深理解。

          功能需求:

          創(chuàng)建一個 SingleLinkedList 類,具備以下功能:

          • append(data) ?將元素添加到鏈表頭,需要參數(shù),無返回值。
          • is_empty() ?檢查單鏈表是否為空,不需要參數(shù),返回布爾值。
          • iter() ?遍歷鏈表。
          • insert(index, vaule) 指定位置刪除。
          • remove(index) 實現(xiàn)移除鏈表指定索引的節(jié)點。
          • size() ?返回單鏈表中元素個數(shù),不需要參數(shù),返回整數(shù)。
          class?Node:
          ????'''定義節(jié)點類'''
          ????def?__init__(self,?data):
          ????????self.data?=?data????#值域,存儲數(shù)據(jù)
          ????????self.next?=?None????#指針域,存儲下一元素的地址

          #定義四個節(jié)點
          a?=?Node(86)
          b?=?Node(19)
          c?=?Node(4)
          d?=?Node(12)
          #最簡單的方法將四個節(jié)點連接
          a.next?=?b
          b.next?=?c
          c.next?=?d
          head?=?a
          #依次輸出節(jié)點的值
          while?head?is?not?None:
          ????print(head.data)
          ????head?=?head.next
          #輸出為?86?19?4?12

          定義一個鏈表類來實現(xiàn)鏈表,同時可以定義對鏈表的簡單操作,比如對鏈表進(jìn)行添加節(jié)點,移除節(jié)點,插入節(jié)點等方法。

          class?LinkedList:
          ????'''定義鏈表類'''
          ????def?__init__(self):????#不需要參數(shù),返回值是空鏈表
          ????????self.head?=?None???#頭節(jié)點
          ????????self.tail?=?None???#尾節(jié)點

          定義類方法,判斷鏈表是否為空。

          def?is_empty(self):????
          ????'''判斷鏈表是否為空'''
          ????return?self.head?is?None

          定義類方法,向鏈表末尾新的節(jié)點。

          def?append(self,?data):????
          ????'''向鏈表里添加節(jié)點'''
          ????node?=?Node(data)??????#創(chuàng)建一個節(jié)點實例
          ????if?self.head?is?None:??#判斷鏈表是否為空
          ?????????self.head?=?node???#如果是空鏈表,添加一個節(jié)點,頭節(jié)點就是尾節(jié)點
          ?????????self.tail?=?node
          ?????else:
          ?????????self.tail.next?=?node??#tail為尾節(jié)點,在尾節(jié)點添加新節(jié)點,尾節(jié)點的指針域
          ?????????self.tail?=?node???????#指向下一節(jié)點,這時新的尾節(jié)點就是node節(jié)點

          定義類方法,實現(xiàn)遍歷鏈表元素。

          def?iter(self):????????????
          ????'''遍歷鏈表,函數(shù)返回的是一個生成器'''
          ????if?not?self.head:????#遍歷從鏈表的頭節(jié)點開始,如果不是,返回
          ????????return
          ????current?=?self.head??#將head節(jié)點賦值給current,當(dāng)前節(jié)點
          ????yield?current.data???#包含yield語句的函數(shù)都被稱為生成器。
          ????while?current.next:
          ????????current?=?current.next
          ????????yield?current.data

          定義類方法,實現(xiàn)在鏈表指定位置插入新的節(jié)點。

          def?insert(self,?index,?vaule):????
          ????'''在鏈表的指定索引插入一個新的節(jié)點'''
          ????current?=?self.head
          ????current_index?=?0????????#head節(jié)點的索引為0
          ????if?self.head?is?None:????#如果鏈表為空
          ????????raise?Exception("The?list?is?an?empty?list")
          ????while?current_index?-1:??#通過while循環(huán)找到指定索引的節(jié)點
          ????????current?=?current.next
          ????????if?current?is?None:??#判斷是否為尾節(jié)點
          ????????????raise?Exception('......')
          ????????current_index?+=?1
          ????node?=?Node(vaule)???????#新建節(jié)點
          ????node.next?=?current.next?#node的指針域指向原節(jié)點的下一節(jié)點
          ????current.next?=?node??????#原節(jié)點的下一節(jié)點指向node
          ????if?node.next?is?None:
          ????????self.tail?=?node

          要向 a 和 b節(jié)點之間插入一個新的節(jié)點 e ,需要將 e.next = a.next,之后再將 a.next = e,這是插入一個新的節(jié)點的關(guān)鍵。

          定義類方法,實現(xiàn)移除鏈表指定索引的節(jié)點。

          def?remove(self,?index):
          ????'''移除鏈表指定索引元素'''
          ????current?=?self.head
          ????current_index?=?0
          ????if?self.head?is?None:??#?空鏈表時
          ????????raise?Exception('The?list?is?an?empty?list')
          ????while?current_index?-1:
          ????????current?=?current.next
          ????????if?current?is?None:
          ????????????raise?Exception('list?length?less?than?index')
          ????????current_index?+=?1
          ????if?index?==?0:???#?當(dāng)刪除第一個節(jié)點時
          ????????self.head?=?current.next
          ????????current?=?current.next
          ????????return
          ????if?self.head?is?self.tail:???#?當(dāng)只有一個節(jié)點的鏈表時
          ????????self.head?=?None
          ????????self.tail?=?None
          ????????return?
          ????current.next?=?current.next.next
          ????if?current.next?is?None:??#?當(dāng)刪除的節(jié)點是鏈表最后一個節(jié)點時
          ????????self.tail?=?current

          定義類方法,返回鏈表的長度(節(jié)點的數(shù)量)。

          def?size(self):
          ????'''返回鏈表長度'''
          ????current?=?self.head
          ????count?=?0
          ????if?current?is?None:
          ????????return?'The?list?is?an?empty?list'
          ????while?current?is?not?None:
          ????????count?+=?1
          ????????current?=?current.next
          ????return?count

          最后,來實現(xiàn)一下鏈表的一些簡單操作。

          if?__name__?==?'__main__':
          ????l?=?LinkedList()????#創(chuàng)建一個鏈表對象,空鏈表
          ????for?i?in?range(10):?#向鏈表里添加節(jié)點
          ????????l.append(i)
          ????print(list(l.iter()))???#遍歷鏈表元素
          ????#輸出為[0,?1,?2,?3,?4,?5,?6,?7,?8,?9]
          ????l.insert(2,?10)????#在索引2處插入10
          ????print(list(l.iter()))???#遍歷鏈表元素
          ????#輸出為[0,?1,?10,?2,?3,?4,?5,?6,?7,?8,?9]
          ????l.remove(0)???????#移除索引為0的節(jié)點
          ????print(list(l.iter()))???#遍歷鏈表元素
          ????#輸出為[1,?2,?3,?4,?5,?6,?7,?8,?9]
          ????print(l.size())???#輸出鏈表的長度?輸出為10

          LeetCode 第 2題:兩數(shù)相加

          給出兩個 非空 的鏈表用來表示兩個非負(fù)的整數(shù)。其中,它們各自的位數(shù)是按照 逆序 的方式存儲的,并且它們的每個節(jié)點只能存儲 一位 數(shù)字。

          #?如果,我們將這兩個數(shù)相加起來,則會返回一個新的鏈表來表示它們的和。?
          #?您可以假設(shè)除了數(shù)字?0?之外,這兩個數(shù)都不會以?0?開頭。?
          #?示例:?
          #?輸入:(2 -> 4 -> 3)?+?(5 -> 6 -> 4)
          #輸出:7 ->?0?-> 8
          #原因:342 + 465 = 807
          #?
          #?Related?Topics?鏈表?數(shù)學(xué)

          思路一:將鏈表轉(zhuǎn)化列表,反轉(zhuǎn)列表 用join方法拼接數(shù)字 切片[::-1],將sum轉(zhuǎn)化成鏈表res

          #?class?ListNode:
          #?????def?__init__(self,?x):
          #?????????self.val?=?x
          #?????????self.next?=?None


          class?Solution:
          ????def?addTwoNumbers(self,?l1:?ListNode,?l2:?ListNode)?->?ListNode:
          ????????#?將鏈表轉(zhuǎn)化列表
          ????????val1,?val2?=?[l1.val],?[l2.val]

          ????????while?l1.next:
          ????????????val1.append(l1.next.val)
          ????????????l1?=?l1.next

          ????????while?l2.next:
          ????????????val2.append(l2.next.val)
          ????????????l2?=?l2.next

          ????????#?反轉(zhuǎn)列表?用join方法拼接數(shù)字?切片[::-1]

          ????????num_1?=?''.join([str(i)?for?i?in?val1[::-1]])
          ????????num_2?=?''.join([str(i)?for?i?in?val2[::-1]])
          ????????sums?=??str(int(num_1)?+?int(num_2))[::-1]
          ????????#?將sum轉(zhuǎn)化成鏈表res

          ????????#?頭節(jié)點
          ????????res??=?head?=?ListNode(int(sums[0]))

          ????????for?i?in?range(1,?len(sums)):
          ????????????#?拼接
          ????????????head.next?=?ListNode(int(sums[i]))
          ????????????head?=?head.next
          ????????return?res


          思路二:將結(jié)果保存在一個新的鏈表中,使用兩個指針,一個指向頭節(jié)點,用于返回結(jié)果,另一個指向當(dāng)前節(jié)點,用于計算并保存結(jié)果。遍歷兩個輸入鏈表,逐位進(jìn)行相加,若某一個鏈表遍歷到了結(jié)尾,則取 0 參與運(yùn)算。每一位的數(shù)字為兩個數(shù)字對應(yīng)位以及進(jìn)位之和除 10 的余數(shù),而該位是否有進(jìn)位則是這個和除 10 的商。

          優(yōu)化:不需要將整個數(shù)的和求出,只需要每一位對應(yīng)相加,

          具體代碼如下:

          class?Solution:
          ????def?addTwoNumbers(self,?l1:?ListNode,?l2:?ListNode)?->?ListNode:
          ???????#?判斷0還是1
          ????????res?=?0
          ????????root?=?n?=?ListNode(0)
          ????
          ????????while?l1?or?l2?or?res:
          ????????????v1?=?v2?=?0
          ????????????if?l1:
          ????????????????v1?=?l1.val
          ????????????????l1?=?l1.next
          ????????????if?l2:
          ????????????????v2?=?l2.val
          ????????????????l2?=?l2.next
          ????????????# divmod()?函數(shù)把除數(shù)和余數(shù)運(yùn)算結(jié)果結(jié)合起來,返回一個包含商和余數(shù)的元組(a // b, a % b)。
          ????????????res,?val?=?divmod(v1?+?v2?+?res,?10)
          ????????????n.next?=?ListNode(val)
          ????????????n?=?n.next
          ????????????
          ????????return?root.next

          「人生最重要的不是所站的位置,而是內(nèi)心所朝的方向。只要我在每篇博文中寫得自己體會,修煉身心;在每天的不斷重復(fù)學(xué)習(xí)中,耐住寂寞,練就真功,不畏艱難,奮勇前行,不忘初心,砥礪前行,人生定會有所收獲,不留遺憾 (作者:Runsen )」

          本文已收錄 GitHub,傳送門~[1] ,里面更有大廠面試完整考點,歡迎 Star。



          參考資料

          [1]

          傳送門~: https://github.com/MaoliRUNsen/runsenlearnpy100


          更多的文章

          點擊下面小程序


          - END -


          瀏覽 39
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  奇米视频7777 | 少妇日韩| 三级片中文字幕在线观看 | 免费看啪啪啪网站 | 女同性恋一区二区 |