##Linked List - 鏈表 鏈表是線性表的一種。線性表是最基本、最簡單、也是最經常使用的一種數據結構。線性表中數據元素之間的關係是一對一的關係,即除了第一個和最後一個數據元素以外,其它數據元素都是首尾相接的。線性表有兩種存儲方式,一種是順序存儲結構,另外一種是鏈式存儲結構。咱們經常使用的數組就是一種典型的順序存儲結構。node
相反,鏈式存儲結構就是兩個相鄰的元素在內存中可能不是相鄰的,每個元素都有一個指針域,指針域通常是存儲着到下一個元素的指針。這種存儲方式的優勢是插入和刪除的時間複雜度爲 O(1),不會浪費太多內存,添加元素的時候纔會申請內存,刪除元素會釋放內存。缺點是訪問的時間複雜度最壞爲 O(n)。數組
順序表的特性是隨機讀取,也就是訪問一個元素的時間複雜度是O(1),鏈式表的特性是插入和刪除的時間複雜度爲O(1)。數據結構
鏈表就是鏈式存儲的線性表。根據指針域的不一樣,鏈表分爲單向鏈表、雙向鏈表、循環鏈表等等。翻譯
請看示例:指針
class listNode: def __init__(self, val): self.val = val self.next = None
##鏈表的基本操做 ###反轉鏈表 ####單向鏈表code
鏈表的基本形式是:1 -> 2 -> 3 -> null,反轉須要變爲 3 -> 2 -> 1 -> null。這裏要注意:內存
示例:ci
class listNode: def __init__(self, val): self.val = val self.next = None def reserse(self, head): prev = None while head: temp = head.next head.next = prev prev = head head = temp return prev
####雙向鏈表it
和單向鏈表的區別在於:雙向鏈表的反轉核心在於next和prev域的交換,還須要注意的是當前節點和上一個節點的遞推。ast
class DListNode: def __init__(self, val): self.val = val self.prev = self.next = null def reverse(self, head): curt = None while head: curt = head head = curt.next curt.next = curt.prev curt.prev = head return curt
###刪除鏈表中的某個節點 刪除鏈表中的某個節點必定須要知道這個點的前繼節點,因此須要一直有指針指向前繼節點。還有一種刪除是僞刪除,是指複製一個和要刪除節點值同樣的節點,而後刪除,這樣就沒必要知道其真正的前繼節點了。
而後只須要把 prev -> next = prev -> next -> next 便可。可是因爲鏈表表頭可能在這個過程當中產生變化,致使咱們須要一些特別的技巧去處理這種狀況。就是下面提到的 Dummy Node。
###鏈表指針的魯棒性 綜合上面討論的兩種基本操做,鏈表操做時的魯棒性問題主要包含兩個狀況:
Dummy node 是一個虛擬節點,也能夠認爲是標杆節點。Dummy node 就是在鏈表表頭 head 前加一個節點指向 head,即 dummy -> head。Dummy node 的使用多針對單鏈表沒有前向指針的問題,保證鏈表的 head 不會在刪除操做中丟失。除此以外,還有一種用法比較少見,就是使用 dummy node 來進行head的刪除操做,好比 Remove Duplicates From Sorted List II,通常的方法current = current.next 是沒法刪除 head 元素的,因此這個時候若是有一個dummy node在head的前面。
因此,當鏈表的 head 有可能變化(被修改或者被刪除)時,使用 dummy node 能夠很好的簡化代碼,最終返回 dummy.next 即新的鏈表。
快慢指針 快慢指針也是一個能夠用於不少問題的技巧。所謂快慢指針中的快慢指的是指針向前移動的步長,每次移動的步長較大即爲快,步長較小即爲慢,經常使用的快慢指針通常是在單鏈表中讓快指針每次向前移動2,慢指針則每次向前移動1。快慢兩個指針都從鏈表頭開始遍歷,因而快指針到達鏈表末尾的時候慢指針恰好到達中間位置,因而能夠獲得中間元素的值。快慢指針在鏈表相關問題中主要有兩個應用:
快速找出未知長度單鏈表的中間節點 設置兩個指針 fast、slow 都指向單鏈表的頭節點,其中fast的移動速度是slow的2倍,當*fast指向末尾節點的時候,slow正好就在中間了。 判斷單鏈表是否有環 利用快慢指針的原理,一樣設置兩個指針 *fast、*slow 都指向單鏈表的頭節點,其中 fast的移動速度是slow的2倍。若是 *fast = NULL,說明該單鏈表 以 NULL結尾,不是循環鏈表;若是 *fast = *slow,則快指針追上慢指針,說明該鏈表是循環鏈表。
###快慢指針 快慢指針也是一個能夠用於不少問題的技巧。所謂快慢指針中的快慢指的是指針向前移動的步長,每次移動的步長較大即爲快,步長較小即爲慢,經常使用的快慢指針通常是在單鏈表中讓快指針每次向前移動2,慢指針則每次向前移動1。快慢兩個指針都從鏈表頭開始遍歷,因而快指針到達鏈表末尾的時候慢指針恰好到達中間位置,因而能夠獲得中間元素的值。快慢指針在鏈表相關問題中主要有兩個應用:
快速找出未知長度單鏈表的中間節點 設置兩個指針 *fast、*slow 都指向單鏈表的頭節點,其中*fast的移動速度是*slow的2倍,當*fast指向末尾節點的時候,slow正好就在中間了。 判斷單鏈表是否有環 利用快慢指針的原理,一樣設置兩個指針 *fast、*slow 都指向單鏈表的頭節點,其中 *fast的移動速度是*slow的2倍。若是 *fast = NULL,說明該單鏈表 以 NULL結尾,不是循環鏈表;若是 *fast = *slow,則快指針追上慢指針,說明該鏈表是循環鏈表。
class NodeCircle: def __init__(self, val): self.val = val self.next = None def has_circle(self, head): slow = head fast = head while (slow and fast): fast = fast.next slow = slow.next if fast: fast = fast.next if fast == slow: break if fast and slow and (fast == slow): return True else: return False