zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    mysql索引创建和使用细节(二)
    mysql索引创建和使用细节(一)
    PHP7.2.6安装sodium扩展
    passwd修改密码失败,报鉴定令牌操作错误
    centos6升级python版本至python3.5
    centos6升级gcc版本
    elasticsearch中文手册
    MySQL主从仅同步指定库
    适用于Centos6/7,vsftp自动安装脚本
    Redis内存模型
  • 原文地址:https://www.cnblogs.com/stacklike/p/8284081.html
Copyright © 2011-2022 走看看