1. 程式人生 > >Python與數據結構[0] -> 鏈表[0] -> 單鏈表與帶表頭單鏈表的 Python 實現

Python與數據結構[0] -> 鏈表[0] -> 單鏈表與帶表頭單鏈表的 Python 實現

per pty traversal main xxd return 是否為空 其中 完整

單鏈表 / Linked List


目錄

  1. 單鏈表
  2. 帶表頭單鏈表

鏈表是一種基本的線性數據結構,在C語言中,這種數據結構通過指針實現,由於存儲空間不要求連續性,因此插入和刪除操作將變得十分快速。下面將利用Python來完成單鏈表的實現。

1 單鏈表

不帶表頭的單鏈表通常形式如下,

node_1 -> node_2 -> node_3 -> node_4

完整代碼

技術分享圖片
  1 class Node:
  2     def __init__(self, val=None, nxt=None):
  3         self.value = val
  4         self.next = nxt
5 6 def __str__(self): 7 return str(self.value) 8 9 10 class LinkedList: 11 """ 12 Linked list: 13 node_1 -> node_2 -> node_3 -> node_4 14 """ 15 def __init__(self, iterable=()): 16 self.header = None 17 if iterable:
18 self.init(iterable) 19 20 def __str__(self): 21 def _traversal(self): 22 node = self.header 23 while node and node.next: 24 yield node 25 node = node.next 26 yield node 27 return ->.join(map(lambda
x: str(x), _traversal(self))) 28 29 def init(self, iterable=()): 30 # Note: use empty tuple rather than list to init iterable 31 if not iterable: 32 return 33 self.header = Node(iterable[0]) # header value 34 node = self.header 35 for i in iterable[1:]: # add all node 36 node.next = Node(i) 37 node = node.next 38 39 def show(self): 40 print(self) 41 42 @property 43 def length(self): 44 if self.header is None: 45 return 0 46 node = self.header # node pointer points to header 47 i = 1 48 while node.next: 49 node = node.next # node pointer move to next 50 i += 1 51 return i 52 53 @property 54 def is_empty(self): 55 return self.header is None 56 57 def clear(self): 58 self.__init__() 59 # self.header = None 60 61 def append(self, item): 62 self.insert(item, self.length) 63 64 def find(self, item): 65 node = self.header 66 while node.next and node.value != item: 67 node = node.next 68 if node.value == item: 69 return node 70 return None 71 72 def find_previous(self, item): 73 node = self.header 74 while node.next and node.next.value != item: 75 node = node.next 76 if node.next and node.next.value == item: 77 return node 78 return None 79 80 def delete(self, item): 81 ‘‘‘ 82 node_1 -- X --> node_2 -----> node_3 83 \ / 84 \ / 85 ------------------ 86 ‘‘‘ 87 prev = self.find_previous(item) 88 if prev: 89 prev.next = prev.next.next 90 91 def insert(self, item, index): 92 ‘‘‘ 93 ----> node_2 --- 94 / 95 / 96 node_1 ------- X ---------> node_3 97 98 ‘‘‘ 99 if abs(index) > self.length: 100 return 101 if index < 0: 102 self.insert(item, self.length+index+1) 103 return 104 elif index == 0: 105 self.insert(self.header.value, 1) 106 self.header.value = item 107 return 108 node = self.header 109 i = 0 110 while i < index-1: 111 node = node.next 112 i += 1 113 n = node.next 114 node.next = Node(item, n) 115 116 117 def test(li): 118 print(Show linked list:) 119 li.show() 120 121 print(\nInit linked list:) 122 li.init([1, 2, 3, 4, 5, 6, xd, 8, 9]) 123 li.show() 124 125 print(\nInsert element:) 126 li.insert(xxd, -3) 127 li.show() 128 129 print(\nAppend element:) 130 li.append(10) 131 li.show() 132 133 e = xd 134 print(\nFind element:) 135 x = li.find(e) 136 print(x.value if x else x) 137 print(\nFind previous element:) 138 x = li.find_previous(e) 139 print(x.value if x else x) 140 141 print(\nDelete element:) 142 li.delete(xd) 143 li.show() 144 145 print(\nFind element not exist:) 146 x = li.find(e) 147 print(x.value if x else x) 148 149 print(\nInsert element to header:) 150 li.insert(cc, 0) 151 li.show() 152 153 print(\nClear linked list:) 154 li.clear() 155 li.show() 156 157 print(\nCurrent length: %s % li.length) 158 print(\nIs empty: %s % li.is_empty) 159 160 161 if __name__ == __main__: 162 test(LinkedList())
View Code

分段解釋

首先,需要定義一個結點類(在C語言中則使用結構體進行定義),其中包括兩個基本屬性,當前值和下個結點。由於Python中無法使用類似C語言中的指針,因此只能通過變量的引用來實現類似指針操作的功能。為了更好的顯示結點,這裏重載了結點的內置函數__str__。

1 class Node:
2     def __init__(self, val=None, nxt=None):
3         self.value = val
4         self.next = nxt
5 
6     def __str__(self):
7         return str(self.value)

接著,我們需要。定義一個鏈表類,並利用前面的結點來構建這個鏈表。鏈表類中包含了一個頭結點屬性,在初始化函數中,首先將頭結點賦值None,類似於在C語言中將頭結點指針指向NULL。初始化方法中定義了一個默認參數,使得鏈表可以在實例化的時候利用傳入的可叠代對象進行鏈表生成。

1 class LinkedList:
2     """
3     Linked list:
4             node_1 -> node_2 -> node_3 -> node_4
5     """
6     def __init__(self, iterable=()):
7         self.header = None
8         if iterable:
9             self.init(iterable)

重載鏈表的__str__方法,在顯示鏈表的時候會對鏈表進行遍歷,然後以更加清晰的方式顯示在輸出終端上。

1     def __str__(self):
2         def _traversal(self):
3             node = self.header
4             while node and node.next:
5                 yield node
6                 node = node.next
7             yield node
8         return ->.join(map(lambda x: str(x), _traversal(self)))

定義鏈表的生成函數,函數中接收可叠代對象並生成新的鏈表。註意這裏最好使用不可變的元組而不是可變的列表。

在生成時,首先將第一個元素作為頭結點,然後依次將每個結點的next屬性引用指向下一個結點。

1     def init(self, iterable=()):
2         # Note: use empty tuple rather than list to init iterable
3         if not iterable:
4             return
5         self.header = Node(iterable[0])     # header value
6         node = self.header
7         for i in iterable[1:]:              # add all node
8             node.next = Node(i)
9             node = node.next

鏈表的show方法,由於已經重載了__str__方法,這裏只需要print自身即可。

1     def show(self):
2         print(self)

鏈表的length屬性方法,用於返回鏈表的長度,利用裝飾器定義成屬性方法,調用時遍歷鏈表計算長度最後返回即可。

 1     @property
 2     def length(self):
 3         if self.header is None:
 4             return 0
 5         node = self.header      # node pointer points to header
 6         i = 1
 7         while node.next:
 8             node = node.next    # node pointer move to next
 9             i += 1
10         return i

鏈表的is_empty屬性方法,用於檢測鏈表是否為空,只需要查看頭結點是否為None即可。

1     @property
2     def is_empty(self):
3         return self.header is None

鏈表的clear方法,用於清空鏈表,可以選擇直接初始化鏈表或將鏈表的頭結點指向None。

1     def clear(self):
2         self.__init__()
3         # self.header = None

鏈表的append方法,用於在鏈表尾部添加一個元素結點,這裏只要直接調用後面的insert方法並插入到鏈表長度的那個位置即可。

1     def append(self, item):
2         self.insert(item, self.length)

鏈表的find方法,用於在鏈表中查找並返回某個結點,查找時會從鏈表的頭部開始,遍歷查找,直到找到查找的值或遇到None,這裏的find方法有兩點需要註意:

  1. 查找是依據元素的值進行查找,返回找到的第一個結點;
  2. 這裏的查找方法如果遇到有環鏈表將無法自行檢測退出

最後返回查找到的結點或None。

1     def find(self, item):
2         node = self.header
3         while node.next and node.value != item:
4             node = node.next
5         if node.value == item:
6             return node
7         return None

find_previous與find類似,返回查找結點的前一個結點。

1     def find_previous(self, item):
2         node = self.header
3         while node.next and node.next.value != item:
4             node = node.next
5         if node.next and node.next.value == item:
6             return node
7         return None

delete方法用於刪除結點,基本思路在於,找到需要刪除結點node_2的前一個結點node_1,將前一個結點node_1的next指針指向刪除結點的下一個結點node_3,從而惰性刪除了這個結點。

查找前一個結點可以使用前面定義的find_previous方法。

 1     def delete(self, item):
 2         ‘‘‘
 3         node_1 -- X --> node_2 -----> node_3
 4             \                    /
 5              \                  /
 6               ------------------
 7         ‘‘‘
 8         prev = self.find_previous(item)
 9         if prev:
10             prev.next = prev.next.next

insert方法用於向鏈表中插入一個結點,

基本思路在於,找到需要插入的位置前後的兩個結點node_1和node_3,將node_1.next指向需要插入的結點node_2,再將node_2.next指向node_3,便完成了插入操作。

若插入的位置為頭結點之前,則會替換原來的頭結點為新插入的結點。

同時,這裏的插入函數還支持index為負數的情況,類似於列表的負數索引。

 1     def insert(self, item, index):
 2         ‘‘‘
 3                 ----> node_2 ---
 4                /                 5               /                   6         node_1 -------  X  ---------> node_3
 7 
 8         ‘‘‘
 9         if abs(index) > self.length:
10             return
11         if index < 0:
12             self.insert(item, self.length+index+1)
13             return
14         elif index == 0:
15             self.insert(self.header.value, 1)
16             self.header.value = item
17             return
18         node = self.header
19         i = 0
20         while i < index-1:
21             node = node.next
22             i += 1
23         n = node.next 
24         node.next = Node(item, n)

最後,定義一個測試函數用於測試鏈表的各種操作。

開始時顯示空表

1 def test(li):
2     print(Show linked list:)
3     li.show()

得到結果

Show linked list:
None

接著初始化鏈表元素

1     print(\nInit linked list:)
2     li.init([1, 2, 3, 4, 5, 6, xd, 8, 9])
3     li.show()

得到結果

Init linked list:
1->2->3->4->5->6->xd->8->9

再向鏈表中插入元素

1     print(\nInsert element:)
2     li.insert(xxd, -3)
3     li.show()

得到結果

Insert element:
1->2->3->4->5->6->xd->xxd->8->9

向鏈表末尾添加元素

1     print(\nAppend element:)
2     li.append(10)
3     li.show()

得到結果

Append element:
1->2->3->4->5->6->xd->xxd->8->9->10

查找元素

1     e = xd
2     print(\nFind element:)
3     x = li.find(e)
4     print(x.value if x else x)
5     print(\nFind previous element:)
6     x = li.find_previous(e)
7     print(x.value if x else x)

得到結果

Find element:
xd

Find previous element:
6

刪除元素

1     print(\nDelete element:)
2     li.delete(xd)
3     li.show()

得到結果

Delete element:
1->2->3->4->5->6->xxd->8->9->10

查找不存在的元素

1     print(\nFind element not exist:)
2     x = li.find(e)
3     print(x.value if x else x)

得到結果

Find element not exist:
None

替換頭結點

1     print(\nInsert element to header:)
2     li.insert(cc, 0)
3     li.show()

得到結果

Insert element to header:
cc->1->2->3->4->5->6->xxd->8->9->10

清空鏈表並查看鏈表信息

1     print(\nClear linked list:)
2     li.clear()
3     li.show()
4 
5     print(\nCurrent length: %s % li.length)
6     print(\nIs empty: %s % li.is_empty)

得到結果

Clear linked list:
None

Current length: 0

Is empty: True

2 帶表頭單鏈表

這種單鏈表帶有一個標誌結點,通常被稱為表頭或啞結點,表頭通常位於位置0處,通常不會被改變。

Linked list with dummy header node:
       Header -> node_1 -> node_2 -> node_3

帶表頭的單鏈表在實現時與不帶表頭的單鏈表類似,因此可以繼承前面的單鏈表類來派生出一個帶表頭單鏈表類,其中需要進行修改和重載的方法主要有生成鏈表、判斷鏈表為空和插入鏈表這幾個方法。

 1 from linked_list import Node, LinkedList, test
 2 
 3 
 4 class LinkedListDummyHeader(LinkedList):
 5     """
 6     Linked list with dummy header node:
 7             Header -> node_1 -> node_2 -> node_3
 8     """
 9     def __init__(self, iterable=()):
10         self.header = Node(Header, None)
11         if iterable:
12             self.init(iterable)
13 
14     def init(self, iterable=()):
15         if not iterable:
16             return
17         node = self.header
18         for i in iterable:
19             node.next = Node(i)
20             node = node.next
21 
22     @property
23     def is_empty(self):
24         return self.header.next is None
25         # if self.length == 1:
26         #     return True
27         # return False
28 
29     def insert(self, item, index):
30         if index == 0:
31             return
32         super(LinkedListDummyHeader, self).insert(item, index)
33 
34 if __name__ == __main__:
35     test(LinkedListDummyHeader())

__init__方法:

初始化時頭結點不再指向None,而是指向表頭。

init方法:

由於有表頭的存在,因此創建初始鏈表的方法首先需要建立一個表頭,再將所有元素依次加入鏈表中。

is_empty方法:

由於表頭的存在,此時判斷是否為空則可以根據表頭指向結點是否為空,或鏈表長度是否為1來進行。

insert方法:

此處需要修改的地方在於當插入點為表頭時,則應該直接返回或引發異常,無法修改表頭。

最後利用單鏈表的測試函數進行測試得到結果

Show linked list:
Header

Init linked list:
Header->1->2->3->4->5->6->xd->8->9

Insert element:
Header->1->2->3->4->5->6->xd->xxd->8->9

Append element:
Header->1->2->3->4->5->6->xd->xxd->8->9->10

Find element:
xd

Find previous element:
6

Delete element:
Header->1->2->3->4->5->6->xxd->8->9->10

Find element not exist:
None

Insert element to header:
Header->1->2->3->4->5->6->xxd->8->9->10

Clear linked list:
Header

Current length: 1

Is empty: True

Python與數據結構[0] -> 鏈表[0] -> 單鏈表與帶表頭單鏈表的 Python 實現