Python與數據結構[0] -> 鏈表[2] -> 鏈表有環與鏈表相交判斷的 Python 實現
阿新 • • 發佈:2018-01-14
lis 退出 測試 htm 判斷鏈表是否有環 += 帶環鏈表 off long
鏈表有環與鏈表相交判斷的 Python 實現
目錄
- 有環鏈表
- 相交鏈表
1 有環鏈表
判斷鏈表是否有環可以參考鏈接,
有環鏈表主要包括以下幾個問題(C語言描述):
- 判斷環是否存在: 可以使用追趕方法,定義兩個指針slow和fast,分別以1步和2步前進,若存在環則兩者會相遇,否則fast遇到NULL時則退出;
- 獲取環的長度:若存在環,則以相遇點為起點,fast和slow再次開始前進,第二次碰相遇slow走過的步數(1圈)即為環長度;
- 找出入環點:相遇點到連接點的距離 = 頭指針到連接點的距離,因此,讓頭指針和slow同時開始前進,相遇的點即為連接點;
- 帶環鏈表長度:問題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:View Code11 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) iffast 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))
分段解釋
首先導入單鏈表類,
1 from linked_list import LinkedList
然後定義一個函數,用於檢測鏈表是否有環,並最終返回4個信息,1. 是否有環,2. 入環點,3. 環長度,4. 鏈表長度,
具體過程為,
- 分別獲取fast、slow和head結點,均以頭結點為起始
- 開始循環,slow和fast分別以1步和2步前進,並記錄slow所走步數
- 每一步都判斷fast和slow是否相遇,此處需要用is而不能用==,這樣才能判斷是否是相同對象(指針引用)
- 當fast遇到None或兩結點相遇時,退出循環,並記錄下是否有環和經過的步數
- 判斷是否有環,若無環則鏈表長度即經過長度的2倍或2倍加1,環長為0
- 若有環,則同時驅動fast(2步)、slow(1步)和head(1步)前進
- 當slow和head相遇的點即為入環點,停止head,slow繼續前進
- 在slow和fast再次相遇的點,記錄走過的長度,即為環長
- 更新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 相交鏈表
判斷鏈表是否相交及交點的方法主要有兩種,
- 遍歷兩個鏈表,得到最後的結點,若兩個結點指向同一個結點(指針相等),則說明兩個鏈表相交,此時記錄下鏈表長度long_length和short_length,以較長的鏈表為起始點,先前進 (long_length - short_length) 步,再驅動兩個結點同時前進,相遇點即為交點。
- 將其中一個鏈表首尾相接,形成一個環,再判斷另一個鏈表是否有環;若有環則入環點即為交點;
利用代碼分別實現上面的兩種判斷方法,
完整代碼
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 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 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 實現