鏈式結構--單鏈表node
1.線性結構和鏈式結構的區別:python
線性結構內存是連續的,能夠經過下標訪問的app
鏈式結構內存是不連續的,沒法經過下標訪問ide
(鏈式結構append時會很方便,find的時候會很麻煩,由於要從頭至尾的遍歷、尋找)
單元測試
經過連接指針的方式,指向下一個。測試
2.單鏈表(LinkedList)
spa
由一個一個節點經過指針的方式,把節點給串起來,root節點是一個入口,能夠去遍歷。指針
每一塊區域都會表明一個值,節點稱之爲node。blog
每一個節點除了value外,還要一個指針指向下一個元素,如圖:節點索引
這裏經過class來定義一個node屬性,它包含值,還包含指向下一個元素的指針,
單鏈表的結構以下:
data:root(入口)、length(長度)
linkedlist
method:init(初始化的方法)、append(追加元素)、appendleft(左側追加元素)、iter_node(遍歷節點)、remove(刪除及誒單)、find(查找節點)、popleft(刪掉頭節點)
代碼實現:
class Node(object): #Node節點 def __init__(self, value=None, next=None): self.value, self.next = value, next #多重賦值 class LinkedList(object): def __init__(self, maxsize=None): #maxsize表示最大容量,None表示無限擴容 self.maxsize = maxsize #賦值 self.root = Node() #須要一個根節點,是一個空節點 self.length = 0 #須要一個長度,默認是0 self.tailnode = None def __len__(self): #魔術方法,定義len方法 return self.length #這裏返回記錄的長度值 def append(self, value): if self.maxsize is not None and \ len(self) > self.maxsize: #條件 raise Exception('LinkedList Is Full') #拋出異常 node = Node(value) #構造新加入的節點,並賦值爲value tailnode = self.tailnode if tailnode is None: #這個代表尾節點是None,則說明鏈表只有root節點這一個節點 self.root.next = node #把root的next指針指向新加入的節點 else: tailnode.next = node #當尾節點不是None的時候,將尾節點的next指針指向新加入節點 self.tailnode = node #更新鏈表,如今新加入的節點是鏈表的尾節點 self.length += 1 #將鏈表長度值加1 def appendleft(self, vlaue): #定義往左邊插入節點,位置爲root節點的後面,實現往左邊插入的操做 headnone = self.root.next #定義頭節點爲root節點的下一個節點 node = Node() #構造新加入的節點 self.root.next = node #把root的next節點指向新加入的節點 node.next = headnone #把新加入的節點的next節點指向原來的頭節點 #這兩步就把鏈表串起來了,實如今root節點後面和原頭節點前面添加節點 def iter_node(self): #遍歷,從頭節點,遍歷到尾節點 curnode = self.root.next #從第一個節點遍歷,curnode爲root節點的下一個節點 while curnode is not self.tailnode: #只要curnode不是尾節點 yield curnode #就一直yield當前節點 curnode = curnode.next #更新curnode爲下一節點 yield curnode #這個while循環只移動到了最後一個節點,而後還要把當前節點更新到最後一個節點, #也要把他yield出來,這樣就實現了完整的遍歷操做。 def __iter__(self): for node in self.iter_node(): yield node.value def remove(self, value): #查找並刪除這個節點,這裏是O(n),必須從頭查到尾,無法經過下標查找到 prevnode = self.root #定義prevnode爲根節點 #curnode = self.root.next for curnode in self.iter_node(): #遍歷鏈表,當前節點爲curnode if curnode.value == value: #若是當前遍歷到的curnode的值和要刪除的值一致 prevnode.next = curnode.next #將prevnode的next指針指向到當前要刪除的curnode節點的下一個節點 if curnode is self.tailnode: #若是這個curnode恰好是尾節點 self.tailnode = prevnode #則將prevnode就是尾節點 del curnode #刪除節點 self.length -= 1 #長度減1 return 1 #代表刪除成功 else: #不然若是當前遍歷到的curnode的值不是要刪除的值 prevnode = curnode #則更新prevnode的位置到遍歷的這個非刪除的節點,並繼續遍歷下一個curnode節點 return 1 #刪除完成返回1 def find(self, value): #查找操做,也是要從頭遍歷,這也是單鏈表查找比較慢的緣由,時間複雜度O(n) index = 0 #索引從0開始 for node in self.iter_node(): #遍歷鏈表 if node.value == value: #若是要查找的值等於這個node的值 return index #返回這個node的索引 index += 1 #索引+1 return -1 #沒有找到指定的值則返回-1 def popleft(self): #至關於把第一個節點刪掉 if self.root.next is None: #若是root節點的下一個節點是None raise Exception('Pop From Empty') #拋出異常 headnode = self.root.next #將root的下一個節點定義爲頭節點 self.root.next = headnode.next #再將root的next指針指向上面頭節點的下一個節點 self.length -= 1 #將長度減1 value = headnode.value #同pop操做最後會返回一個值,並打印出來 del headnode #刪除原來的頭節點 return value #返回被popleft掉的值 def clean(self): #清空操做,清空鏈表 for node in self.iter_node(): #遍歷節點,並刪除遍歷到的節點 del node self.root.next = None #將root節點的下一個節點指向空 self.length = 0 #將長度值置0 #單元測試 def test_linked_list(): lst = LinkedList() lst.append(0) lst.append(1) lst.append(2) assert len(lst) == 3 assert lst.find(2) == 2 assert lst.find(3) == -1 lst.remove(0) assert len(lst) == 2 assert lst.find(0) == -1 assert list(lst) == [1, 2] #斷言list只有[1, 2] lst.appendleft(0) assert list(lst) == [0 ,1 ,2] assert len(lst) == 3 headvalue = lst.popleft() #驗證popleft方法 assert headvalue == 0 assert len(lst) == 2 assert list(lst) == [1, 2] lst.clean() #清空鏈表 assert len(lst) == 0
# pytest linked_list.py
執行測試
平均時間複雜度:
鏈表操做 | 平均時間複雜度 |
---|---|
linked_list.append(value) | O(1) |
linked_list.appendleft(value) | O(1) |
linked_list.find(value) | O(n) |
linked_list.remove(value) | O(n) |