找到单链表中环的入口
这是一个很经典的面试题目。
首先,我们要检测一个链表是否包含环。
典型的思路,快慢指针。快指针每次移动两个结点,慢指针移动一个结点。就像两辆车在环形跑道上比赛,速度差一倍,那么快的肯定会追上慢的(套圈)。
如果快的指针到头了,两者还是没有相遇,那么说明这个链表不包含环。
var slow = head;
var fast = head;
while (fast != null && fast.Next != null)
{
slow = slow.Next;
fast = fast.Next.Next;
if (slow == fast) // 相遇了
{
break;
}
}
// 快的指针到头了,说明没有环,也就无所谓环的起始结点,所以返回 null
if (fast == null || fast.Next == null)
{
return null;
}
我们需要知道,他们在哪里相遇了呢?
假设非环部分的长度是 ,那么,慢指针走了 步,位于环的起始位置,快指针走了 步,除去非环部分的 步,快指针在环里面走了 步,考虑 可能比环的长度大,我们可以认为,快指针从环起始位置往前走了 步,也就是说,快指针比慢指针快了 步,从另一个角度看,快指针比慢指针慢了 步。
之后,慢指针走 步,快指针走 步,两者相遇。相遇点距离环的起始位置距离是 (从环的起始结点向前走)。
找到了相遇位置,如何利用这个信息找到环的起始结点呢?
从相遇点再走多远能到环的起始点呢?。
和 有什么关系呢?模。。那么,从另外一个角度看,从链表开始到环的开始之间是 步;从相遇点继续向前,也是 步就到了环的起始结点,无非是在环里面绕零圈,还是多绕几圈的区别。
知道了这个我们就可以解决这个问题了。当找到相遇点之后,把慢指针拿到链表开头,快指针不动,但是每次只移动一步,当两个指针再次相遇的时候,就到了环起始结点。