赞
踩
题目描述:如果一个链表中包含环,如何找出环的入口节点?如图所示,环的入口节点是节点3。
首先第一步,我们应该确定链表中是否包含环。可以使用快慢指针来解决这个问题,定义两个指针,同时从链表的头节点出发,快指针一次走两步,慢指针一次走一步,如果快指针走到了链表的末尾(NULL)说明链表是无环的,因为如果有环的话最后的结点不会是NULL而是环的开始。如果快指针追上慢指针,说明链表有环。
这里如果链表有环,快指针一定是能够追到慢指针的。假设快指针走后,距离慢指针距离为n,那么因为链表有环(可以理解为操场跑圈),快指针每走一步会经过两个节点,而慢指针只会经过一个节点,因此快指针每走一步与慢指针的距离都会-1,最终的距离为
n
−
1
,
n
−
2
,
⋯
,
2
,
1
,
0
n-1,n-2,\cdots,2,1,0
n−1,n−2,⋯,2,1,0。因为距离每次会减一,所以最终会到0,即二者相遇。
然后就是要找到环的入口。这里还是用两个指针来解决问题。我们首先思考一下,如果链表的总长度为n,那么指向链表头节点的指针走过n-1步就到达链表的末尾(以上图为例,从1号节点出发,走5步可以到6号节点)那么再走一步就到达了环的开头,即3号节点。也就是说头指针走n步可以到达环的入口。
我们假设在环开头前的结点数量为x,环中的结点数量为y,那么有
n
=
x
+
y
n=x+y
n=x+y,即6=2+4。假设有两个指针
p
1
,
p
2
p_1,p_2
p1,p2指向头节点,
p
1
p_1
p1先走
y
y
y步,到达节点5,距离走到环的开头还需走
n
−
y
=
x
n-y=x
n−y=x步,也就是再走2步。如果此时
p
2
p_2
p2从头节点出发,那么再走
x
x
x步即可和
p
1
p_1
p1相遇。因此,我们可以先让
p
1
p_1
p1先走
y
y
y步,然后
p
2
p_2
p2和
p
1
p_1
p1同时移动,相遇时一定是环的入口节点,并且同时移动的距离为
x
x
x。
这里之所以这么做而不是直接让
p
1
p_1
p1或
p
2
p_2
p2直接向前走
x
x
x步,是因为代码实现时计算
x
x
x并不方便,因为想要计算
x
x
x,需要知道环的入口节点在哪,而我们的目标就是求环的入口节点,因此求
x
x
x没有意义。而计算
y
y
y更方便,因此实际上并不知道
x
x
x的大小,当然也不知道
n
n
n的大小。前面只是为了好分析将
n
n
n和
x
x
x实际大小写了出来,实际代码实现只有
y
y
y。
此时我们的思路已经明确了,我们需要做三件事:(1)通过快慢指针来判断链表是否有环;(2)计算出环中的结点;(3)定义两个指针,其中一个指针先走“环中结点数量”步,然后第二个指针和第一个指针同时移动,相遇时即是环的入口位置。
在LeetCode提交代码如下所示:
# Definition for singly-linked list. # class ListNode: # def __init__(self, x): # self.val = x # self.next = None class Solution: # 通过快慢指针判断链表是否有环 def isCircle(self,head): if (head == None): return False # 定义快慢指针 fast_pointer = head slow_pointer = head # 如果链表只有一个节点,说明没有环 if(slow_pointer.next == None): return False while(fast_pointer != None): slow_pointer = slow_pointer.next fast_pointer = fast_pointer.next if(fast_pointer == None): return False fast_pointer = fast_pointer.next if(slow_pointer == fast_pointer): return slow_pointer return False # 判断环中节点的数量 def circleNum(self,node): temp_node = node node = node.next count = 1 while(node != temp_node): count += 1 node = node.next return count def detectCycle(self, head: ListNode) -> ListNode: circle_node = self.isCircle(head) if(circle_node == False): return None else: circle_num = self.circleNum(circle_node) p_1 = head p_2 = head # p_1先走circle_num步 for i in range(circle_num): p_1 = p_1.next while(p_1 != p_2): p_1 = p_1.next p_2 = p_2.next return p_1
运行结果如下所示:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。