• Python与数据结构[0] -> 链表/LinkedList[2] -> 链表有环与链表相交判断的 Python 实现


    链表有环与链表相交判断的 Python 实现


    目录

    1. 有环链表
    2. 相交链表

    有环链表

    判断链表是否有环可以参考链接

    有环链表主要包括以下几个问题(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

  • 相关阅读:
    不务正业系列-浅谈《过气堡垒》,一个RTS玩家的视角
    [LeetCode] 54. Spiral Matrix
    [LeetCode] 40. Combination Sum II
    138. Copy List with Random Pointer
    310. Minimum Height Trees
    4. Median of Two Sorted Arrays
    153. Find Minimum in Rotated Sorted Array
    33. Search in Rotated Sorted Array
    35. Search Insert Position
    278. First Bad Version
  • 原文地址:https://www.cnblogs.com/stacklike/p/8284081.html
Copyright © 2020-2023  润新知