1. 程式人生 > >Python與數據結構[0] -> 鏈表[2] -> 鏈表有環與鏈表相交判斷的 Python 實現

Python與數據結構[0] -> 鏈表[2] -> 鏈表有環與鏈表相交判斷的 Python 實現

lis 退出 測試 htm 判斷鏈表是否有環 += 帶環鏈表 off long

鏈表有環與鏈表相交判斷的 Python 實現


目錄

  1. 有環鏈表
  2. 相交鏈表

1 有環鏈表

判斷鏈表是否有環可以參考鏈接,

有環鏈表主要包括以下幾個問題(C語言描述):

  1. 判斷環是否存在: 可以使用追趕方法,定義兩個指針slow和fast,分別以1步和2步前進,若存在環則兩者會相遇,否則fast遇到NULL時則退出;
  2. 獲取環的長度:若存在環,則以相遇點為起點,fast和slow再次開始前進,第二次碰相遇slow走過的步數(1圈)即為環長度;
  3. 找出入環點:相遇點到連接點的距離 = 頭指針到連接點的距離,因此,讓頭指針和slow同時開始前進,相遇的點即為連接點;
  4. 帶環鏈表長度:問題3的連接點與頭指針長度 + 問題2的環長度即為總長。

下面為關於有環鏈表幾個問題的具體實現代碼,

完整代碼

技術分享圖片
 1 from linked_list import LinkedList
 2 
 3 
 4 def check_loop(chain):
 5     has_loop, entry_node, loop_length, chain_length = False, None, 0, 0
 6 
 7     # Get header for fast and slow
 8     step = 0
 9     fast = slow = head = chain.header
10     while fast and fast.next:
11 fast = fast.next.next 12 slow = slow.next 13 step += 1 14 # Note: 15 # Do remember to use ,is‘ rather than ‘==‘ here (assure the id is same). 16 if fast is slow: 17 break 18 has_loop = not(fast is None or fast.next is None) 19 pass_length = (step * 2) if
fast is None else (step * 2 + 1) 20 21 if has_loop: 22 step = 0 23 while True: 24 if head is slow: 25 entry_node = slow 26 pass_length = step 27 if not entry_node: 28 head = head.next 29 fast = fast.next.next 30 slow = slow.next 31 step += 1 32 if fast is slow: 33 break 34 loop_length = step 35 36 chain_length = pass_length + loop_length 37 return has_loop, entry_node, loop_length, chain_length 38 39 40 if __name__ == __main__: 41 print(------------ Loop check ------------------) 42 print(‘‘‘ 43 0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 44 ‘‘‘) 45 loop_chain = LinkedList(range(10)) 46 print(Linked list has loop: %s, entry node: %s, loop length: %s, chain length: %s % check_loop(loop_chain)) 47 48 # Create a loop for linked list. 49 print(‘‘‘ 50 _____________________________ 51 | | 52 V | 53 0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 54 ‘‘‘) 55 node_9 = loop_chain.find(9) 56 node_3 = loop_chain.find(3) 57 node_9.next = node_3 58 print(Linked list has loop: %s, entry node: %s, loop length: %s, chain length: %s % check_loop(loop_chain))
View Code

分段解釋

首先導入單鏈表類,

1 from linked_list import LinkedList

然後定義一個函數,用於檢測鏈表是否有環,並最終返回4個信息,1. 是否有環,2. 入環點,3. 環長度,4. 鏈表長度,

具體過程為,

  1. 分別獲取fast、slow和head結點,均以頭結點為起始
  2. 開始循環,slow和fast分別以1步和2步前進,並記錄slow所走步數
  3. 每一步都判斷fast和slow是否相遇,此處需要用is而不能用==,這樣才能判斷是否是相同對象(指針引用)
  4. 當fast遇到None或兩結點相遇時,退出循環,並記錄下是否有環和經過的步數
  5. 判斷是否有環,若無環則鏈表長度即經過長度的2倍或2倍加1,環長為0
  6. 若有環,則同時驅動fast(2步)、slow(1步)和head(1步)前進
  7. 當slow和head相遇的點即為入環點,停止head,slow繼續前進
  8. 在slow和fast再次相遇的點,記錄走過的長度,即為環長
  9. 更新pass_length及鏈表長度信息,並返回結果
 1 def check_loop(chain):
 2     has_loop, entry_node, loop_length, chain_length = False, None, 0, 0
 3 
 4     # Get header for fast and slow
 5     step = 0
 6     fast = slow = head = chain.header
 7     while fast and fast.next:
 8         fast = fast.next.next
 9         slow = slow.next
10         step += 1
11         # Note:
12         # Do remember to use ,is‘ rather than ‘==‘ here (assure the id is same).
13         if fast is slow:    
14             break
15     has_loop = not(fast is None or fast.next is None)
16     pass_length = (step * 2) if fast is None else (step * 2 + 1)
17 
18     if has_loop:
19         step = 0
20         while True:
21             if head is slow:
22                 entry_node = slow
23                 pass_length = step
24             if not entry_node:
25                 head = head.next
26             fast = fast.next.next
27             slow = slow.next
28             step += 1
29             if fast is slow:
30                 break
31         loop_length = step
32 
33     chain_length = pass_length + loop_length
34     return has_loop, entry_node, loop_length, chain_length

完成函數定義後,首先生成一個基本鏈表,檢測是否有環,

1 if __name__ == __main__:
2     print(------------ Loop check ------------------)
3     print(‘‘‘
4     0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9
5     ‘‘‘)
6     loop_chain = LinkedList(range(10))
7     print(Linked list has loop: %s, entry node: %s, loop length: %s, chain length: %s % check_loop(loop_chain))

得到結果

------------ Loop check ------------------

    0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9
    
Linked list has loop: False, entry node: None, loop length: 0, chain length: 10

再將鏈表的3和9結點相接,形成一個新的有環鏈表,然後利用函數進行判斷。

 1     # Create a loop for linked list.
 2     print(‘‘‘
 3                     _____________________________
 4                    |                             |
 5                    V                             |
 6     0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9
 7     ‘‘‘)
 8     node_9 = loop_chain.find(9)
 9     node_3 = loop_chain.find(3)
10     node_9.next = node_3
11     print(Linked list has loop: %s, entry node: %s, loop length: %s, chain length: %s % check_loop(loop_chain))

得到結果

                    _____________________________
                   |                             |
                   V                             |
    0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9
    
Linked list has loop: True, entry node: 3, loop length: 7, chain length: 10

2 相交鏈表

判斷鏈表是否相交及交點的方法主要有兩種,

  1. 遍歷兩個鏈表,得到最後的結點,若兩個結點指向同一個結點(指針相等),則說明兩個鏈表相交,此時記錄下鏈表長度long_length和short_length,以較長的鏈表為起始點,先前進 (long_length - short_length) 步,再驅動兩個結點同時前進,相遇點即為交點。
  2. 將其中一個鏈表首尾相接,形成一個環,再判斷另一個鏈表是否有環;若有環則入環點即為交點;

利用代碼分別實現上面的兩種判斷方法,

完整代碼

技術分享圖片
 1 from linked_list import LinkedList
 2 from linked_list_loop_check import check_loop
 3 
 4 
 5 def check_intersect_one(c_1, c_2):
 6     def _traversal(c):
 7         node = c.header
 8         while node and node.next:
 9             yield node
10             node = node.next
11         yield node
12 
13     is_intersect, intersect_node = False, None
14     # Get tail node and length
15     step_1 = step_2 = 0
16     for node_1 in _traversal(c_1):
17         step_1 += 1
18     for node_2 in _traversal(c_2):
19         step_2 += 1
20     tail_1, length_1 = node_1, step_1
21     tail_2, length_2 = node_2, step_2
22 
23     if tail_1 is tail_2:
24         # Intersected, fetch the first same node encountered as intersect node.
25         is_intersect = True
26         offset = length_1 - length_2
27         long, short = (_traversal(c_1), _traversal(c_2)) if offset >= 0 else (_traversal(c_2), _traversal(c_1))
28         for i in range(offset):
29             next(long)
30         for node_1, node_2 in zip(long, short):
31             if node_1 is node_2:
32                 break
33         intersect_node = node_1
34     return is_intersect, intersect_node
35 
36 
37 def check_intersect_two(c_1, c_2):
38     def _traversal(c):
39         node = c.header
40         while node and node.next:
41             yield node
42             node = node.next
43         yield node
44 
45     # Create a loop for one of linked lists.
46     for node in _traversal(c_1): pass
47     node.next = c_1.header
48     is_intersect, intersect_node = check_loop(c_2)[:2]
49     # Un-loop
50     node.next = None
51     return is_intersect, intersect_node
52 
53 
54 if __name__ == __main__:
55     print(------------ intersect check ------------------)
56     print(‘‘‘
57     chain_1:  0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6
58     chain_2:  3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 10 -> 11 -> 12 -> 13
59     ‘‘‘)
60     chain_1 = LinkedList(range(7))
61     chain_2 = LinkedList(range(3, 14))
62     print(Linked lists are intersected: %s, intersected node is: %s % check_intersect_one(chain_1, chain_2))
63     print(Linked lists are intersected: %s, intersected node is: %s % check_intersect_two(chain_1, chain_2))
64 
65     # Merge two linked lists
66     print(‘‘‘Merge two linked lists:
67     chain_1:  0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 _
68                                                \69     chain_2:                 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 10 -> 11 -> 12 -> 13
70     ‘‘‘)
71     node_6 = chain_1.find(6)
72     node_7 = chain_2.find(7)
73     node_6.next = node_7
74 
75     # Method one:
76     print(Linked lists are intersected: %s, intersected node is: %s % check_intersect_one(chain_1, chain_2))
77     # Method two:
78     print(Linked lists are intersected: %s, intersected node is: %s % check_intersect_two(chain_1, chain_2))
View Code

分段解釋

首先導入鏈表類和有環檢測函數,

1 from linked_list import LinkedList
2 from linked_list_loop_check import check_loop

接著定義第一種檢測方法,

  1. 定義遍歷函數,用於遍歷鏈表
  2. 分別遍歷兩個鏈表,記錄下步數即為鏈表長度,最後的結點即鏈表尾結點
  3. 判斷尾結點是否相同,若不同則不想交
  4. 若相同則根據長度判斷,讓長鏈表先前進長度差的步數
  5. 隨後同時前進兩個鏈表,找到第一個相遇點即為相交結點。
 1 def check_intersect_one(c_1, c_2):
 2     def _traversal(c):
 3         node = c.header
 4         while node and node.next:
 5             yield node
 6             node = node.next
 7         yield node
 8 
 9     is_intersect, intersect_node = False, None
10     # Get tail node and length
11     step_1 = step_2 = 0
12     for node_1 in _traversal(c_1):
13         step_1 += 1
14     for node_2 in _traversal(c_2):
15         step_2 += 1
16     tail_1, length_1 = node_1, step_1
17     tail_2, length_2 = node_2, step_2
18 
19     if tail_1 is tail_2:
20         # Intersected, fetch the first same node encountered as intersect node.
21         is_intersect = True
22         offset = length_1 - length_2
23         long, short = (_traversal(c_1), _traversal(c_2)) if offset >= 0 else (_traversal(c_2), _traversal(c_1))
24         for i in range(offset):
25             next(long)
26         for node_1, node_2 in zip(long, short):
27             if node_1 is node_2:
28                 break
29         intersect_node = node_1
30     return is_intersect, intersect_node

再定義第二種檢測方法,

  1. 定義遍歷函數,遍歷其中一個鏈表並找到尾結點
  2. 首尾相接形成一個環
  3. 判斷另一個鏈表是否有環,並獲取結果信息
  4. 解除前面的鏈表環,還原鏈表,並返回結果
 1 def check_intersect_two(c_1, c_2):
 2     def _traversal(c):
 3         node = c.header
 4         while node and node.next:
 5             yield node
 6             node = node.next
 7         yield node
 8 
 9     # Create a loop for one of linked lists.
10     for node in _traversal(c_1): pass
11     node.next = c_1.header
12     is_intersect, intersect_node = check_loop(c_2)[:2]
13     # Un-loop
14     node.next = None
15     return is_intersect, intersect_node

最後,通過下面的函數進行測試,首先生成兩個不相交的鏈表並用兩種方法進行判斷,接著講其中一個鏈表和另一個鏈表相交,再進行判斷。

 1 if __name__ == __main__:
 2     print(------------ intersect check ------------------)
 3     print(‘‘‘
 4     chain_1:  0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6
 5     chain_2:  3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 10 -> 11 -> 12 -> 13
 6     ‘‘‘)
 7     chain_1 = LinkedList(range(7))
 8     chain_2 = LinkedList(range(3, 14))
 9     print(Linked lists are intersected: %s, intersected node is: %s % check_intersect_one(chain_1, chain_2))
10     print(Linked lists are intersected: %s, intersected node is: %s % check_intersect_two(chain_1, chain_2))
11 
12     # Merge two linked lists
13     print(‘‘‘Merge two linked lists:
14     chain_1:  0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 _
15                                                \16     chain_2:                 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 10 -> 11 -> 12 -> 13
17     ‘‘‘)
18     node_6 = chain_1.find(6)
19     node_7 = chain_2.find(7)
20     node_6.next = node_7
21 
22     # Method one:
23     print(Linked lists are intersected: %s, intersected node is: %s % check_intersect_one(chain_1, chain_2))
24     # Method two:
25     print(Linked lists are intersected: %s, intersected node is: %s % check_intersect_two(chain_1, chain_2))

輸出結果

------------ intersect check ------------------

    chain_1:  0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6
    chain_2:  3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 10 -> 11 -> 12 -> 13
    
Linked lists are intersected: False, intersected node is: None
Linked lists are intersected: False, intersected node is: None
Merge two linked lists:
    chain_1:  0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 _
                                                   chain_2:                 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 10 -> 11 -> 12 -> 13
    
Linked lists are intersected: True, intersected node is: 7
Linked lists are intersected: True, intersected node is: 7

參考鏈接


http://blog.csdn.net/liuxialong/article/details/6555850

http://www.cppblog.com/humanchao/archive/2008/04/17/47357.html

Python與數據結構[0] -> 鏈表[2] -> 鏈表有環與鏈表相交判斷的 Python 實現