<kbd id="5sdj3"></kbd>
<th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>

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

    共 4703字,需瀏覽 10分鐘

     ·

    2020-12-29 09:52


    「@Author:Runsen」

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

    鏈表 Linked List

    鏈表由許多結(jié)點(也可以叫節(jié)點或元素)組成,每一個結(jié)點有兩個域,左邊部份叫值域 data,用于存放用戶數(shù)據(jù);右邊叫指針域 next,一般是存儲著到下一個元素的指針。head結(jié)點(頭節(jié)點),head是一個特殊的結(jié)節(jié),head結(jié)點永遠指向第一個結(jié)點,tail結(jié)點(尾節(jié)點),tail結(jié)點也是一個特殊的結(jié)點,tail結(jié)點永遠指向最后一個節(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ù)雜度達到O(N)

    優(yōu)點:

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

    缺點:

    • 隨機訪問效率低,時間復(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)鏈表,同時可以定義對鏈表的簡單操作,比如對鏈表進行添加節(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ù)相加

    給出兩個 非空 的鏈表用來表示兩個非負的整數(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é)果。遍歷兩個輸入鏈表,逐位進行相加,若某一個鏈表遍歷到了結(jié)尾,則取 0 參與運算。每一位的數(shù)字為兩個數(shù)字對應(yīng)位以及進位之和除 10 的余數(shù),而該位是否有進位則是這個和除 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ù)運算結(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
    點贊
    評論
    收藏
    分享

    手機掃一掃分享

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

    手機掃一掃分享

    分享
    舉報

    <kbd id="5sdj3"></kbd>
    <th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>
    91丨国产丨精品丨丝袜 | 国产一级a爱做片免费 | 丁香五月婷婷六月 | 日韩AA片 | 青娱乐A V |