题目描述:
单链表有环指的是单链表中某个结点的next指针域指向的是链表中在它之前的某一个结点,这样在链表的尾部形成一个环形结构。如何判断单链表是否有环存在?
方法:
1. 蛮力法
定义一个HashSet来存放结点的引用,并将其初始化为空,从链表的头结点开始遍历,每遍历到一个结点就判断HashSet中是否有这个结点的引用。如果没有,说明这个点是第一次访问,还没有形成环,那么将这个结点的引用添加到指针HashSet中;如果在HashSet中找到了同样的结点,那么说明这个结点已经被访问过了,于是就形成了环。这种方法的时间复杂度为O(n),空间复杂度也为O(n)
2. 快慢指针遍历法
定义两个指针 fast 和 slow ,二者的初始值都指向链表头,指针 slow 每次前进一步,指针 fast 每次前进两步,两个指针同时向前移动,快指针每移动一次都要跟慢指针比较,如果快指针等于慢指针,就证明这两个链表是带环的单向链表;否则证明这个链表不是带环的循环链表。实现代码见后面引申部分。
引申:
如果链表存在环,那么如何找出环的入口点?
思路:
当链表有环的时候,如果知道环的入口点,那么在需要遍历链表或释放链表所占空间的时候方法将会非常简单,下面主要介绍查找链表环入口点的思路:
如果单链表有环,那么按照上述方法二的思路,当走的快的指针 fast 与走的慢的指针 slow 相遇时,slow 指针肯定没有遍历完链表,而 fast 指针已经在环内循环了n ( n>=1 ) 圈。如果 slow 指针走了 s 步,则 fast 指针走了 2s 步( fast 步数还等于 s 加上在环上多转的 n 圈),设环的长度为 r,则满足下述关系表达式:
2s = s + nr,即得到 s = nr
设整个链表长度为 L ,入口环与相遇点距离 x,起点到环入口点的距离为 a,则满足下列关系表达式:
a + x = nr
a + x = (n-1)r +r = (n-1)r + L - a
a = (n-1)r + (L - a - x)
(L-a-x)为相遇点到环入口点的距离,从链表头到环入口点的距离=(n-1)*环长+相遇点到环入口点的长度,于是从链表头与相遇点分别设一个指针,每次各走一步,两个指针必定相遇,且相遇第一点为环入口点
。
算法性能分析:
这种方法只需要对链表进行一次遍历,因此,时间复杂度为O(n);
另外由于只需要几个指针变量来保存结点的地址信息,所以空间复杂度为O(1)
实现代码如下:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2020/1/16 21:35
# @Author : buu
# @Software: PyCharm
# @Blog :https://blog.csdn.net/weixin_44321080
class LNode:
def __init__(self, data=None, next=None):
self.data = data
self.next = next
def ConstructList():
"""
构造一个带环的单链表
:return: 链表的头结点
"""
i = 1
head = LNode()
tmp = None
cur = head
while i < 8:
tmp = LNode(i)
cur.next = tmp
cur = tmp
i += 1
cur.next = head.next.next.next # 构造环
return head
def isLoop(head):
"""
判断单链表是否有环
:param head: 头结点
:return: None: 无环,否则返回slow与fast相遇点的结点;
"""
if head is None or head.next is None:
return None
slow = head.next
fast = head.next # 初始时fast与slow都指向链表的第一个结点
while fast is not None and fast.next is not None:
slow = slow.next
fast = fast.next.next
if slow == fast:
return slow
return None
def findLoopNode(head, meetNode):
"""
找出环的入口点
:param head: 头结点
:param meetNode: 快慢指针相遇的结点
:return: None:无环,否则返回slow与fast指针相遇点的结点
"""
first = head.next
second = meetNode
while first != second:
first = first.next
second = second.next
return first
if __name__ == '__main__':
head = ConstructList()
cur = head.next
i = 0
print('lindedList:', end=' ')
while i < 7:
print(cur.data, end='->')
cur = cur.next
i += 1
print(cur.data, end='')
meetNode = isLoop(head)
loopNode = None
if meetNode != None:
print('\nhas loop!')
loopNode = findLoopNode(head, meetNode)
print('entry:' + str(loopNode.data))
else:
print('no loop!')
结果:
end