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


目录

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

有环链表

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

有环链表主要包括以下几个问题(C语言描述):

  1. 判断环是否存在: 可以使用追赶方法,定义两个指针slow和fast,分别以1步和2步前进,若存在环则两者会相遇,否则fast遇到NULL时则退出;
  2. 获取环的长度:若存在环,则以相遇点为起点,fast和slow再次开始前进,第二次碰相遇slow走过的步数(1圈)即为环长度;
  3. 找出入环点:相遇点到连接点的距离 = 头指针到连接点的距离,因此,让头指针和slow同时开始前进,相遇的点即为连接点;
  4. 带环链表长度:问题3的连接点与头指针长度 + 问题2的环长度即为总长。

下面为关于有环链表几个问题的具体实现代码,

完整代码

 from linked_list import LinkedList

 def check_loop(chain):
has_loop, entry_node, loop_length, chain_length = False, None, 0, 0 # Get header for fast and slow
step = 0
fast = slow = head = chain.header
while fast and fast.next:
fast = fast.next.next
slow = slow.next
step += 1
# Note:
# Do remember to use ,is' rather than '==' here (assure the id is same).
if fast is slow:
break
has_loop = not(fast is None or fast.next is None)
pass_length = (step * 2) if fast is None else (step * 2 + 1) if has_loop:
step = 0
while True:
if head is slow:
entry_node = slow
pass_length = step
if not entry_node:
head = head.next
fast = fast.next.next
slow = slow.next
step += 1
if fast is slow:
break
loop_length = step chain_length = pass_length + loop_length
return has_loop, entry_node, loop_length, chain_length if __name__ == '__main__':
print('------------ Loop check ------------------')
print('''
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9
''')
loop_chain = LinkedList(range(10))
print('Linked list has loop: %s, entry node: %s, loop length: %s, chain length: %s' % check_loop(loop_chain)) # Create a loop for linked list.
print('''
_____________________________
| |
V |
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9
''')
node_9 = loop_chain.find(9)
node_3 = loop_chain.find(3)
node_9.next = node_3
print('Linked list has loop: %s, entry node: %s, loop length: %s, chain length: %s' % check_loop(loop_chain))

分段解释

首先导入单链表类,

 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及链表长度信息,并返回结果
 def check_loop(chain):
has_loop, entry_node, loop_length, chain_length = False, None, 0, 0 # Get header for fast and slow
step = 0
fast = slow = head = chain.header
while fast and fast.next:
fast = fast.next.next
slow = slow.next
step += 1
# Note:
# Do remember to use 'is' rather than '==' here (assure the id is same).
if fast is slow:
break
has_loop = not(fast is None or fast.next is None)
pass_length = (step * 2) if fast is None else (step * 2 + 1) if has_loop:
step = 0
while True:
if head is slow:
entry_node = slow
pass_length = step
if not entry_node:
head = head.next
fast = fast.next.next
slow = slow.next
step += 1
if fast is slow:
break
loop_length = step chain_length = pass_length + loop_length
return has_loop, entry_node, loop_length, chain_length

完成函数定义后,首先生成一个基本链表,检测是否有环,

 if __name__ == '__main__':
print('------------ Loop check ------------------')
print('''
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9
''')
loop_chain = LinkedList(range(10))
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结点相接,形成一个新的有环链表,然后利用函数进行判断。

     # Create a loop for linked list.
print('''
_____________________________
| |
V |
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9
''')
node_9 = loop_chain.find(9)
node_3 = loop_chain.find(3)
node_9.next = node_3
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. 将其中一个链表首尾相接,形成一个环,再判断另一个链表是否有环;若有环则入环点即为交点;

利用代码分别实现上面的两种判断方法,

完整代码

 from linked_list import LinkedList
from linked_list_loop_check import check_loop def check_intersect_one(c_1, c_2):
def _traversal(c):
node = c.header
while node and node.next:
yield node
node = node.next
yield node is_intersect, intersect_node = False, None
# Get tail node and length
step_1 = step_2 = 0
for node_1 in _traversal(c_1):
step_1 += 1
for node_2 in _traversal(c_2):
step_2 += 1
tail_1, length_1 = node_1, step_1
tail_2, length_2 = node_2, step_2 if tail_1 is tail_2:
# Intersected, fetch the first same node encountered as intersect node.
is_intersect = True
offset = length_1 - length_2
long, short = (_traversal(c_1), _traversal(c_2)) if offset >= 0 else (_traversal(c_2), _traversal(c_1))
for i in range(offset):
next(long)
for node_1, node_2 in zip(long, short):
if node_1 is node_2:
break
intersect_node = node_1
return is_intersect, intersect_node def check_intersect_two(c_1, c_2):
def _traversal(c):
node = c.header
while node and node.next:
yield node
node = node.next
yield node # Create a loop for one of linked lists.
for node in _traversal(c_1): pass
node.next = c_1.header
is_intersect, intersect_node = check_loop(c_2)[:2]
# Un-loop
node.next = None
return is_intersect, intersect_node if __name__ == '__main__':
print('------------ intersect check ------------------')
print('''
chain_1: 0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6
chain_2: 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 10 -> 11 -> 12 -> 13
''')
chain_1 = LinkedList(range(7))
chain_2 = LinkedList(range(3, 14))
print('Linked lists are intersected: %s, intersected node is: %s' % check_intersect_one(chain_1, chain_2))
print('Linked lists are intersected: %s, intersected node is: %s' % check_intersect_two(chain_1, chain_2)) # Merge two linked lists
print('''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
''')
node_6 = chain_1.find(6)
node_7 = chain_2.find(7)
node_6.next = node_7 # Method one:
print('Linked lists are intersected: %s, intersected node is: %s' % check_intersect_one(chain_1, chain_2))
# Method two:
print('Linked lists are intersected: %s, intersected node is: %s' % check_intersect_two(chain_1, chain_2))

分段解释

首先导入链表类和有环检测函数,

 from linked_list import LinkedList
from linked_list_loop_check import check_loop

接着定义第一种检测方法,

  1. 定义遍历函数,用于遍历链表
  2. 分别遍历两个链表,记录下步数即为链表长度,最后的结点即链表尾结点
  3. 判断尾结点是否相同,若不同则不想交
  4. 若相同则根据长度判断,让长链表先前进长度差的步数
  5. 随后同时前进两个链表,找到第一个相遇点即为相交结点。
 def check_intersect_one(c_1, c_2):
def _traversal(c):
node = c.header
while node and node.next:
yield node
node = node.next
yield node is_intersect, intersect_node = False, None
# Get tail node and length
step_1 = step_2 = 0
for node_1 in _traversal(c_1):
step_1 += 1
for node_2 in _traversal(c_2):
step_2 += 1
tail_1, length_1 = node_1, step_1
tail_2, length_2 = node_2, step_2 if tail_1 is tail_2:
# Intersected, fetch the first same node encountered as intersect node.
is_intersect = True
offset = length_1 - length_2
long, short = (_traversal(c_1), _traversal(c_2)) if offset >= 0 else (_traversal(c_2), _traversal(c_1))
for i in range(offset):
next(long)
for node_1, node_2 in zip(long, short):
if node_1 is node_2:
break
intersect_node = node_1
return is_intersect, intersect_node

再定义第二种检测方法,

  1. 定义遍历函数,遍历其中一个链表并找到尾结点
  2. 首尾相接形成一个环
  3. 判断另一个链表是否有环,并获取结果信息
  4. 解除前面的链表环,还原链表,并返回结果
 def check_intersect_two(c_1, c_2):
def _traversal(c):
node = c.header
while node and node.next:
yield node
node = node.next
yield node # Create a loop for one of linked lists.
for node in _traversal(c_1): pass
node.next = c_1.header
is_intersect, intersect_node = check_loop(c_2)[:2]
# Un-loop
node.next = None
return is_intersect, intersect_node

最后,通过下面的函数进行测试,首先生成两个不相交的链表并用两种方法进行判断,接着讲其中一个链表和另一个链表相交,再进行判断。

 if __name__ == '__main__':
print('------------ intersect check ------------------')
print('''
chain_1: 0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6
chain_2: 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 10 -> 11 -> 12 -> 13
''')
chain_1 = LinkedList(range(7))
chain_2 = LinkedList(range(3, 14))
print('Linked lists are intersected: %s, intersected node is: %s' % check_intersect_one(chain_1, chain_2))
print('Linked lists are intersected: %s, intersected node is: %s' % check_intersect_two(chain_1, chain_2)) # Merge two linked lists
print('''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
''')
node_6 = chain_1.find(6)
node_7 = chain_2.find(7)
node_6.next = node_7 # Method one:
print('Linked lists are intersected: %s, intersected node is: %s' % check_intersect_one(chain_1, chain_2))
# Method two:
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] -> 链表/LinkedList[2] -> 链表有环与链表相交判断的 Python 实现的更多相关文章

  1. Python与数据结构[0] -> 链表/LinkedList[0] -> 单链表与带表头单链表的 Python 实现

    单链表 / Linked List 目录 单链表 带表头单链表 链表是一种基本的线性数据结构,在C语言中,这种数据结构通过指针实现,由于存储空间不要求连续性,因此插入和删除操作将变得十分快速.下面将利 ...

  2. Python与数据结构[0] -> 链表/LinkedList[1] -> 双链表与循环双链表的 Python 实现

    双链表 / Doubly Linked List 目录 双链表 循环双链表 1 双链表 双链表和单链表的不同之处在于,双链表需要多增加一个域(C语言),即在Python中需要多增加一个属性,用于存储指 ...

  3. python的数据结构分类,以及数字的处理函数,类型判断

    python的数据结构分类: 数值型 int:python3中都是长整形,没有大小限制,受限内存区域的大小 float:只有双精度型 complex:实数和虚数部分都是浮点型,1+1.2J bool: ...

  4. python基础笔记-0

    python中数据结构,主要有列表.元组.字典.集合. python中最基本数据结构是序列(sequence).序列中每个元素被分配一个序号——即元素位置,也成为索引.第一个索引是0,第二个是1,以此 ...

  5. Python与数据结构[1] -> 栈/Stack[0] -> 链表栈与数组栈的 Python 实现

    栈 / Stack 目录 链表栈 数组栈 栈是一种基本的线性数据结构(先入后出FILO),在 C 语言中有链表和数组两种实现方式,下面用 Python 对这两种栈进行实现. 1 链表栈 链表栈是以单链 ...

  6. 用Python实现数据结构之链表

    链表 链表与栈,队列不一样,它是由一个个节点构成的,每个节点存储着本身的一些信息,也存储着其他一个或多个节点的引用,可以从一个节点找到其他的节点,节点与节点之间就像是有链连在一起一样,这种数据结构就叫 ...

  7. python实现数据结构单链表

    #python实现数据结构单链表 # -*- coding: utf-8 -*- class Node(object): """节点""" ...

  8. Java数据结构-线性表之单链表LinkedList

    线性表的链式存储结构,也称之为链式表,链表:链表的存储单元能够连续也能够不连续. 链表中的节点包括数据域和指针域.数据域为存储数据元素信息的域,指针域为存储直接后继位置(一般称为指针)的域. 注意一个 ...

  9. 数据结构之链表(LinkedList)(三)

    数据结构之链表(LinkedList)(二) 环形链表 顾名思义 环形列表是一个首尾相连的环形链表 示意图 循环链表的特点是无须增加存储量,仅对表的链接方式稍作改变,即可使得表处理更加方便灵活. 看一 ...

随机推荐

  1. “管中窥豹”,MyCAT的基因缺陷

    提起MyCAT,我的脑海里,总是浮现出这样一首偈.弘忍觉得自己老了,需要找一个接班人,于是,弘忍要求弟子们每人写一个偈子,根据偈子观察各位弟子的开悟程度,从而传授正宗禅宗的衣钵.弟子们心里其实都很明白 ...

  2. 《Cracking the Coding Interview》——第1章:数组和字符串——题目5

    2014-03-18 01:40 题目:对字符串进行类似游程编码的压缩,如果压缩完了长度更长,则返回不压缩的结果.比如:aabcccccaaa->a2b1c5a3,abc->abc. 解法 ...

  3. 孤荷凌寒自学python第五十二天初次尝试使用python读取Firebase数据库中记录

    孤荷凌寒自学python第五十二天初次尝试使用python读取Firebase数据库中记录 (完整学习过程屏幕记录视频地址在文末) 今天继续研究Firebase数据库,利用google免费提供的这个数 ...

  4. 爬虫:Scrapy11 - Logging

    Scrapy 提供了 log 功能.可以通过 scrapy.log 模块使用.当前底层实现使用了 Twisted logging,不过可能在之后会有所变化. log 服务必须通过显式调用 scrapy ...

  5. HDU 4031 Attack (线段树)

    成功袭击次数=所有袭击次数-成功防守次数 需要一个辅助pre来记录上一次袭击成功什么时候,对于每个查询,从上一次袭击成功开始,每隔t更新一次. 感觉这样做最坏时间复杂度是O(n^2),这里 说是O(q ...

  6. spring 配置问题记录1-@ResponseBody和favorPathExtension

    在搭建springmvc+easyui的项目时,有一个地方参照网上说的方法一直没实现出来, 就是前台的datagrid的数据渲染不上去, 尝试了好多种方法,包括也找了目前手里的项目来进行比较,也没发现 ...

  7. 华为手机怎么安装Google

    华为手机怎么安装google 新买了个华为荣耀九,结果安装Google Play提示gms core 步骤一 gms 安装器.应用市场已经下架了  地址:链接: 点击打开链接 密码: m63j 步骤二 ...

  8. min_free_kbytes是内存最安全值的阈值,然后这个值是怎么影响到系统内存回收的呢?

    min_free_kbytes 内存域水印值:min_free_kbytes 当不设置的时候:sqrt(16M)=4k 4k*4 = 16k 设置内存水印值的函数是: 6792 /* 6793 * I ...

  9. Spring 对属性文件的加密与解密

    一般用于配置密码等敏感信息 解密/加密工具类 package com.baobaotao.placeholder; import sun.misc.BASE64Decoder; import sun. ...

  10. 以jhtml结尾的文件

    用一个实例来说明,直接上代码. LogonAction.java(一个servlet) package com.lz.web.action; import java.io.IOException; i ...