给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。为了表示给定链表中的环,使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
说明:不允许修改给定的链表。
示例:
输入:head = [3,2,0,-4],pos = 1
输出:1
解释:链表中有环,其尾部连接到第二个节点
该题的意思首先是判断一个链表当中存不存在环(不存在的话返回null),其次输出该链表中存在的环的起点位置。
具体解题思路如《代码随想录》所示。我将用更加通俗易懂的讲法解释这个思路,思路是,利用快慢双指针法。
首先,定义一个快指针fast,一个慢指针slow,这两个指针都从头结点开始出发,唯一不同的点在于,fast指针每次前进两格,slow每次每次前进一格。在这种情况下,如果链表存在环的话,fast指针必然先进入环,slow指针后进入环。slow指针进入环时,fast可能还未走完一圈环,也有可能已经走了好几圈环。但在slow指针进入环后,由于slow每次前进一格,fast每次前进两格,所以slow指针和fast指针必然在环中相遇,因此fast指针和slow指针相遇了便证明了该链表存在环。
接着,需要找到链表中环的起点。
如图所示,我们可以假设快慢节点在红星处相遇,根据简单的数学知识,此时慢节点slow走了(x+y)个长度节点,而快节点fast走了x+y+n(y+z)个长度节点。由于快节点每次前进两格,慢节点每次前进一格,所以快节点前进的长度是慢节点的两倍,即x+y+n(y+z)=2*(x+y),化简移项后可以得到入口节点x=(n-1)*(y+z)+z,其中n代表fast在环中转的圈数。
求出x的关系式又代表什么呢?我们可以看见从头结点到入口节点之间的长度为x,x又等于(n-1)个环的长度再加上一个z的长度,这就可以理解为一个指针从头结点出发,一个指针从快慢指针相交处出发时,当两个指针相遇时,相遇的点就在入口节点处。这个关系式能记住就记住,记不住大不了画个图自己推导一下。
根据以上思路,我们可以去分析完整代码,完整代码如下:
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
slow = head
fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
# If there is a cycle, the slow and fast pointers will eventually meet
if slow == fast:
# Move one of the pointers back to the start of the list
slow = head
while slow != fast:
slow = slow.next
fast = fast.next
return slow
# If there is no cycle, return None
return None
理解思路后,代码分析起来就没有难度了,首先定义快慢节点,定义它们的移动方式,当快慢节点相遇时,将slow移到了头结点head,而fast节点依旧在相遇节点。接着重新设置两个节点的移动方式,都改为一格一格移动,只要slow和fast指针没有相遇,那便一直向后移动,直到两个指针相遇,即相遇在入口节点,return到slow,其实return到fast也可以。