類:節點NODEpython
用鏈表實現的基本模塊是節點。每一個節點對象必須持有至少兩條信息。首先,節點必須包含列表元素自己。
咱們將這稱爲該節點的「數據區」(data field)。此外,每一個節點必須保持到下一個節點的引用。
示例1 顯示了Python 的實現方法。須要指出,咱們將一般如下圖 所示的方式表明一個節點對象。
節點類還包括訪問和修改的經常使用方法:返回節點
數據和引用到下一項。數組
class Node: def __init__(self, initdata): self.data = initdata self.next = None def getData(self): return self.data def getNext(self): return self.next def setData(self, newdata): self.data = newdata def setNext(self, newnext): self.next = newnext
節點類對象包含自己的項目數據和對下一個節點的引用app
咱們以常見的方式建立了節點類。測試
>>> temp = Node(66) >>> temp.getData() 66 >>>
Python 的特殊值None 將在節點類和以後的鏈表類中發揮重要的做用。引用None 意味着沒有下一個節點。
在構造器中,一個節點的對下一節點引用的初始賦值是None。由於這有時被稱爲把節點「接地」(grounding),
咱們在圖中將使用標準化的「接地」標誌來表示一個值爲None 的引用。
用None 來做爲你在初始化時對下一個節點的引用是一個極妙的主意。spa
無序列表 UNORDERED LIST3d
無序列表將由一個節點集合組成,每個節點採用顯式引用連接到下一個節點。
只要咱們知道第一個節點的位置(包含第一項),在這以後的每一個元素均可以經過如下鏈
接找到下一個節點。爲實現這個想法,UnorderedList 類必須保持一個對第一節點的引用。指針
代碼2 顯示了這種構造結構。注意每一個UnorderedList 對象將保持列表的頭一個節點的引用。orm
class UnorderedList: def __init__(self): self.head = None
實例化:對象
>>> mylist = UnorderedList()
在最初當咱們創建一個列表時,其中沒有任何元素。這些賦值語句創建了一個鏈接好的鏈表。
正如咱們在定義節點類時討論到的,特殊引用——None 會再被用來講明列表的頭不指向任何東西。
最終,如圖所示,以前給出的示例列表將由一個鏈表來表示。列表的頭指向包含列表的第一項的第一節點。
以此類推,該節點有一個指針指向到下一個節點(的內容)。須要注意的一點是,
列表類自己不包含任何節點。取而代之的是,它包含對鏈式存儲結構的第一個節點的引用。blog
is_empty(判斷鏈表是否爲空)方法僅僅只是檢查了列表的頭是否指向None。其結果以布爾值表示,
若是鏈表中沒有節點,self.head==None 的結果只能是True。由於新鏈表爲空,因此構造器和
是否爲空的檢驗必須與另外一個保持一致。這個顯示了使用指向None 來指示鏈表結構的結束。在
Python 中,None 能夠與任何指向相提並論。若是兩個指向同一個物體,那麼他們是平等的。
def isEmpyty(self): return self.head == None
從鏈表頭部新增節點:
def add(self, item): temp = Node(item) temp.setNext(self.head) self.head = temp
列表中的每一個元素一定屬於一個節點。第二行建立了一個新的節點並將插入的元素做爲節點的數據。
如今咱們必須經過連接這個新的節點與原有的結構來完成插入元素的工做。
接下來咱們所要實現的方法——求元素個數(size)、查找(search)和 移除(remove),所有
是基於一個叫作鏈表的遍歷(traversal)的操做的。遍歷指的是有序地訪問每個節點的過程。爲了
作到這一點,咱們可使用一個外部引用,它最開始指向列表的第一個節點。每當咱們訪問一個節點時,
咱們經過「側向移動」(traversing)到下一個引用的方式,將外部引用移動到下一個節點。
爲了實現「size 求元素個數」的方法,咱們須要遍歷鏈表,而且記錄出現過的節點個數。
def size(self): current = self.head count = 0 while current is not None: count += 1 current = current.getNext() return count
咱們把這個外部引用稱爲「當前」(current),
在一個無序表中查詢一個數值這一方法的實現一樣須要用到遍歷。每當咱們訪問一個鏈表中的節點時,
咱們會判斷儲存在此的數據是否就是咱們所要找的元素。
事實上,若是咱們的工做進行到了列表的底端,這意味着咱們所要尋找的元素在列表中並不存在。
一樣,若是咱們找到了那個元素,那麼就沒有必要繼續尋找了。
def search(self, item): current = self.head found = False while current is not None and not found: if current.getData() == item: found = True else: current = current.getNext() return found
「移除(remove)」這個方法須要兩個步驟。
當found 爲真(True)時,current 將會是對包含了要移除元素的一個引用。但咱們要如何移除它?
解決這個難題的方法是,在遍歷鏈表時使用兩個外部引用。current 不變,仍然標記當前遍歷到的位置。
新加入的引用——咱們叫「前一個」(previous)——在遍歷過程當中老是落後於current 一個節點。
這樣,當current 停在待刪除節點時,previous 即停在鏈表中須要修改的節點處。
def remove(self, item): current = self.head previous = None found = False while not found: if current.getData() == item: found = True else: previous = current current = current.getNext() if previous is None: self.head = current.getNext() else: previous.setNext(current.getNext())
完整的移除(remove)過程:
移除(remove)工做中的查詢步驟一旦完成,咱們須要從鏈表中移除那個節點。圖3.21顯示了一個必須進行改動的鏈接。
然而這裏又有一點須要特別說明。
若是要移除的那個元素剛好是列表中的第一個,那麼current會引用鏈表第一個節點。這也就意味着previous的引用會是None。
咱們以前提到,previous要指向那個引用要發生變化的節點。在這種狀況下,須要改動的不是previous,而是鏈表的頭節點(如圖3.22)。
代碼7的第12行讓咱們能夠檢查咱們所要處理的狀況是不是上述的特殊狀況。
鏈表反轉
方法一:
對於一個長度爲n的單鏈表list,用一個大小爲n的數組arr儲存從單鏈表從頭至尾遍歷的全部元素,
在從arr尾到頭讀取元素簡歷一個新的單鏈表; 時間消耗O(n),空間消耗O(n)
class Node: def __init__(self, initdata): self.data = initdata self.next = None def reverse_linkedlist1(head): if head == None or head.next == None: # 邊界條件 return head arr = [] # 空間消耗爲n, n 爲單鏈表的長度 while head: arr.append(head.data) head = head.next newhead = Node(0) tmp = newhead for i in arr[::-1]: tmp.next = Node(i) tmp = tmp.next return newhead.next
方法二:
開始以單鏈表的第二個元素爲循環變量,用2個變量循環向前操做,
並設置1個輔助變量tmp,保存數據; 時間消耗O(n),空間消耗O(1);
def reverse_linkedlist2(head): if head == None or head.next == None: # 邊界條件 return head p1 = head # 循環變量1 p2 = head.next # 循環變量2 tmp = None # 保存數據的臨時變量 while p2: tmp = p2.next p2.next = p1 p1 = p2 p2 = tmp head.next = None return p1
測試:
def create_all(arr): pre = Node(0) tmp = pre for i in arr: tmp.next = Node(i) tmp = tmp.next return pre.next def print_all(head): tmp = head result = [] while tmp: result.append(tmp.data) tmp = tmp.next print(result) a = create_all(range(5)) print_all(a) b = reverse_linkedlist1(a) print_all(b) c = reverse_linkedlist2(a) print_all(c)