1. 程式人生 > >Python與數據結構[0] -> 鏈表[1] -> 雙鏈表與循環雙鏈表的 Python 實現

Python與數據結構[0] -> 鏈表[1] -> 雙鏈表與循環雙鏈表的 Python 實現

ont dual from fin @property end all 自身 lan

雙鏈表 / Doubly Linked List


目錄

  1. 雙鏈表
  2. 循環雙鏈表

1 雙鏈表

雙鏈表和單鏈表的不同之處在於,雙鏈表需要多增加一個域(C語言),即在Python中需要多增加一個屬性,用於存儲指向前一個結點的信息。

Doubly linked list:
       node_1 <---> node_2 <---> node_3

完整代碼

技術分享圖片
 1 from linked_list import LinkedList, test
 2 
 3 
 4 class NodeDual:
 5     def __init__(self, val=None, nxt=None, pre=None):
6 self.value = val 7 self.next = nxt 8 self.prev = pre 9 10 def __str__(self): 11 return <NodeDual, prev=%s, value=%s, next=%s> % (self.prev.value if self.prev else self.prev, 12 self.value, 13
self.next.value if self.next else self.next) 14 15 16 class DoublyLinkedList(LinkedList): 17 """ 18 Doubly linked list: 19 node_1 <---> node_2 <---> node_3 20 """ 21 def __str__(self): 22 def
_traversal(self): 23 node = self.header 24 while node and node.next: 25 yield node 26 node = node.next 27 yield node 28 return <->\n.join(map(lambda x: str(x), _traversal(self))) 29 30 def init(self, iterable=()): 31 if not iterable: 32 return 33 self.header = NodeDual(iterable[0]) # header value 34 pre = None 35 node = self.header 36 for i in iterable[1:]: # add all node 37 node.prev = pre 38 node.next = NodeDual(i) 39 pre = node 40 node = node.next 41 node.prev = pre 42 43 def find_previous(self, item): 44 return self.find(item).prev 45 46 def delete(self, item): 47 pre = self.find_previous(item) 48 if pre: 49 pre.next = pre.next.next 50 pre.next.prev = pre 51 52 def insert(self, item, index): 53 if abs(index) > self.length: 54 return 55 if index < 0: 56 self.insert(item, self.length+index+1) 57 return 58 elif index == 0: 59 self.insert(self.header.value, 1) 60 self.header.value = item 61 return 62 node = self.header 63 i = 0 64 while i < index-1: 65 node = node.next 66 i += 1 67 n = node.next 68 node.next = NodeDual(item, nxt=n, pre=node) 69 if n: 70 n.prev = node.next 71 72 73 if __name__ == __main__: 74 test(DoublyLinkedList())
View Code

分段解釋

雙鏈表可以的基本特性與單鏈表相同,因此可以繼承單鏈表。首先導入前面寫好的單鏈表類和測試函數。

1 from linked_list import LinkedList, test

然後定義一個雙向結點,包含前後兩個屬性,用於存放前後結點的信息。同時重定義__str__方法,在顯示結點時顯示該結點的前後結點及自身值,從而方便查看。

 1 class NodeDual:
 2     def __init__(self, val=None, nxt=None, pre=None):
 3         self.value = val
 4         self.next = nxt
 5         self.prev = pre
 6 
 7     def __str__(self):
 8         return <NodeDual, prev=%s, value=%s, next=%s> % (self.prev.value if self.prev else self.prev,
 9                                                            self.value, 
10                                                            self.next.value if self.next else self.next)

定義雙鏈表類,基類為單鏈表,構造函數可以使用單鏈表的構造函數。

 1 class DoublyLinkedList(LinkedList):
 2     """
 3     Doubly linked list:
 4             node_1 <---> node_2 <---> node_3
 5     """
 6     def __str__(self):
 7         def _traversal(self):
 8             node = self.header
 9             while node and node.next:
10                 yield node
11                 node = node.next
12             yield node
13         return <->\n.join(map(lambda x: str(x), _traversal(self)))

init方法,與單鏈表不同的地方在於,添加結點時需要同時修改結點的前後屬性(引用指針)。

 1     def init(self, iterable=()):
 2         if not iterable:
 3             return
 4         self.header = NodeDual(iterable[0])     # header value
 5         pre = None
 6         node = self.header
 7         for i in iterable[1:]:                  # add all node
 8             node.prev = pre
 9             node.next = NodeDual(i)
10             pre = node
11             node = node.next
12         node.prev = pre

find_previous方法,查找前一個結點的方法將變得很簡單,只需要找到需要找的結點後,通過結點獲取前一個結點即可。

1     def find_previous(self, item):
2         return self.find(item).prev

刪除和插入類似於單鏈表的思路,只不過需要多處理一個前驅結點引用。

 1     def delete(self, item):
 2         pre = self.find_previous(item)
 3         if pre:
 4             pre.next = pre.next.next
 5             pre.next.prev = pre
 6 
 7     def insert(self, item, index):
 8         if abs(index) > self.length:
 9             return
10         if index < 0:
11             self.insert(item, self.length+index+1)
12             return
13         elif index == 0:
14             self.insert(self.header.value, 1)
15             self.header.value = item
16             return
17         node = self.header
18         i = 0
19         while i < index-1:
20             node = node.next
21             i += 1
22         n = node.next
23         node.next = NodeDual(item, nxt=n, pre=node)
24         if n:
25             n.prev = node.next

同樣利用測試單鏈表的函數對雙鏈表進行測試,

1 if __name__ == __main__:
2     test(DoublyLinkedList())

得到結果

Show linked list:
None

Init linked list:
<NodeDual, prev=None, value=1, next=2><->
<NodeDual, prev=1, value=2, next=3><->
<NodeDual, prev=2, value=3, next=4><->
<NodeDual, prev=3, value=4, next=5><->
<NodeDual, prev=4, value=5, next=6><->
<NodeDual, prev=5, value=6, next=xd><->
<NodeDual, prev=6, value=xd, next=8><->
<NodeDual, prev=xd, value=8, next=9><->
<NodeDual, prev=8, value=9, next=None>

Insert element:
<NodeDual, prev=None, value=1, next=2><->
<NodeDual, prev=1, value=2, next=3><->
<NodeDual, prev=2, value=3, next=4><->
<NodeDual, prev=3, value=4, next=5><->
<NodeDual, prev=4, value=5, next=6><->
<NodeDual, prev=5, value=6, next=xd><->
<NodeDual, prev=6, value=xd, next=xxd><->
<NodeDual, prev=xd, value=xxd, next=8><->
<NodeDual, prev=xxd, value=8, next=9><->
<NodeDual, prev=8, value=9, next=None>

Append element:
<NodeDual, prev=None, value=1, next=2><->
<NodeDual, prev=1, value=2, next=3><->
<NodeDual, prev=2, value=3, next=4><->
<NodeDual, prev=3, value=4, next=5><->
<NodeDual, prev=4, value=5, next=6><->
<NodeDual, prev=5, value=6, next=xd><->
<NodeDual, prev=6, value=xd, next=xxd><->
<NodeDual, prev=xd, value=xxd, next=8><->
<NodeDual, prev=xxd, value=8, next=9><->
<NodeDual, prev=8, value=9, next=10><->
<NodeDual, prev=9, value=10, next=None>

Find element:
xd

Find previous element:
6

Delete element:
<NodeDual, prev=None, value=1, next=2><->
<NodeDual, prev=1, value=2, next=3><->
<NodeDual, prev=2, value=3, next=4><->
<NodeDual, prev=3, value=4, next=5><->
<NodeDual, prev=4, value=5, next=6><->
<NodeDual, prev=5, value=6, next=xxd><->
<NodeDual, prev=6, value=xxd, next=8><->
<NodeDual, prev=xxd, value=8, next=9><->
<NodeDual, prev=8, value=9, next=10><->
<NodeDual, prev=9, value=10, next=None>

Find element not exist:
None

Insert element to header:
<NodeDual, prev=None, value=cc, next=1><->
<NodeDual, prev=cc, value=1, next=2><->
<NodeDual, prev=1, value=2, next=3><->
<NodeDual, prev=2, value=3, next=4><->
<NodeDual, prev=3, value=4, next=5><->
<NodeDual, prev=4, value=5, next=6><->
<NodeDual, prev=5, value=6, next=xxd><->
<NodeDual, prev=6, value=xxd, next=8><->
<NodeDual, prev=xxd, value=8, next=9><->
<NodeDual, prev=8, value=9, next=10><->
<NodeDual, prev=9, value=10, next=None>

Clear linked list:
None

Current length: 0

Is empty: True

2 循環雙鏈表

循環雙鏈表類似於雙鏈表,但是將表的頭尾兩個結點相連,形成了一個循環鏈表結構,即頭結點的前一個結點為尾結點,尾結點的下一個結點為頭結點。

    Doubly linked list with loop:
         ________________________________________________________
        |                                                        |
        <===> node_1 <---> node_2 <---> node_3 <---> node_4 <===>
        |________________________________________________________|

首先導入需要的結點和雙鏈表類以及測試函數,

1 from linked_list_doubly import NodeDual, DoublyLinkedList, test

接著定義循環雙鏈表類,對鏈表的顯示輸出同樣要先遍歷鏈表,而這裏的遍歷函數需要額外增加一個判斷條件,即當再次遇到頭結點時,遍歷停止,否則將無限循環遍歷下去。

 1 class DoublyLinkedListLoop(DoublyLinkedList):
 2     """
 3     Doubly linked list with loop:
 4          ________________________________________________________
 5         |                                                        |
 6         <===> node_1 <---> node_2 <---> node_3 <---> node_4 <===>
 7         |________________________________________________________|
 8     """
 9     def __str__(self):
10         def _traversal(self):
11             node = self.header
12             while node and node.next is not self.header:
13                 yield node
14                 node = node.next
15             yield node
16         return <->\n.join(map(lambda x: str(x), _traversal(self)))

循環雙鏈表的生成函數比雙鏈表多了一個步驟,即將雙鏈表的頭尾結點相接。

 1     def init(self, iterable=()):
 2         if not iterable:
 3             return
 4         self.header = NodeDual(iterable[0])     # header value
 5         pre = None
 6         node = self.header
 7         for i in iterable[1:]:                  # add all node
 8             node.prev = pre
 9             node.next = NodeDual(i)
10             pre = node
11             node = node.next
12         node.prev = pre
13 
14         node.next = self.header
15         self.header.prev = node

用於獲取表長度的屬性方法同樣需要更改,增加一個頭結點判斷來終止循環遍歷。

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

兩個查找函數也是同樣需要加入頭結點判斷來停止循環。

 1     def find(self, item):
 2         node = self.header
 3         while node.next is not self.header and node.value != item:
 4             node = node.next
 5         if node.value == item:
 6             return node
 7         return None
 8 
 9     def find_previous(self, item):
10         node = self.header
11         while node.next is not self.header and node.next.value != item:
12             node = node.next
13         if node.next is not self.header and node.next.value == item:
14             return node
15         return None

最後用測試函數進行測試,

if __name__ == __main__:
    test(DoublyLinkedListLoop())

測試結果為

Show linked list:
None

Init linked list:
<NodeDual, prev=9, value=1, next=2><->
<NodeDual, prev=1, value=2, next=3><->
<NodeDual, prev=2, value=3, next=4><->
<NodeDual, prev=3, value=4, next=5><->
<NodeDual, prev=4, value=5, next=6><->
<NodeDual, prev=5, value=6, next=xd><->
<NodeDual, prev=6, value=xd, next=8><->
<NodeDual, prev=xd, value=8, next=9><->
<NodeDual, prev=8, value=9, next=1>

Insert element:
<NodeDual, prev=9, value=1, next=2><->
<NodeDual, prev=1, value=2, next=3><->
<NodeDual, prev=2, value=3, next=4><->
<NodeDual, prev=3, value=4, next=5><->
<NodeDual, prev=4, value=5, next=6><->
<NodeDual, prev=5, value=6, next=xd><->
<NodeDual, prev=6, value=xd, next=xxd><->
<NodeDual, prev=xd, value=xxd, next=8><->
<NodeDual, prev=xxd, value=8, next=9><->
<NodeDual, prev=8, value=9, next=1>

Append element:
<NodeDual, prev=10, value=1, next=2><->
<NodeDual, prev=1, value=2, next=3><->
<NodeDual, prev=2, value=3, next=4><->
<NodeDual, prev=3, value=4, next=5><->
<NodeDual, prev=4, value=5, next=6><->
<NodeDual, prev=5, value=6, next=xd><->
<NodeDual, prev=6, value=xd, next=xxd><->
<NodeDual, prev=xd, value=xxd, next=8><->
<NodeDual, prev=xxd, value=8, next=9><->
<NodeDual, prev=8, value=9, next=10><->
<NodeDual, prev=9, value=10, next=1>

Find element:
xd

Find previous element:
6

Delete element:
<NodeDual, prev=10, value=1, next=2><->
<NodeDual, prev=1, value=2, next=3><->
<NodeDual, prev=2, value=3, next=4><->
<NodeDual, prev=3, value=4, next=5><->
<NodeDual, prev=4, value=5, next=6><->
<NodeDual, prev=5, value=6, next=xxd><->
<NodeDual, prev=6, value=xxd, next=8><->
<NodeDual, prev=xxd, value=8, next=9><->
<NodeDual, prev=8, value=9, next=10><->
<NodeDual, prev=9, value=10, next=1>

Find element not exist:
None

Insert element to header:
<NodeDual, prev=10, value=cc, next=1><->
<NodeDual, prev=cc, value=1, next=2><->
<NodeDual, prev=1, value=2, next=3><->
<NodeDual, prev=2, value=3, next=4><->
<NodeDual, prev=3, value=4, next=5><->
<NodeDual, prev=4, value=5, next=6><->
<NodeDual, prev=5, value=6, next=xxd><->
<NodeDual, prev=6, value=xxd, next=8><->
<NodeDual, prev=xxd, value=8, next=9><->
<NodeDual, prev=8, value=9, next=10><->
<NodeDual, prev=9, value=10, next=cc>

Clear linked list:
None

Current length: 0

Is empty: True

相關閱讀


1. 單鏈表

Python與數據結構[0] -> 鏈表[1] -> 雙鏈表與循環雙鏈表的 Python 實現