原题
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
理解
一段链表,无父指针,其内可能存在环形结构,即遍历时会在某个位置的下一节点指向已经遍历过的某个位置,求发生错误位置
解
非原创解法
设定两个指针。一快(f)(一次走两步)一慢(s)(一次一步),一同出发,因环形链表,两点会在环形内相遇
图中,a为起始点到环形开始点的路程,b为进入环形后,相遇点到环形开始点距离,c为环形总长去掉b的距离,途中红色竖线为相遇点,黄色表示末尾点指向之前的点,也是就是指向环型开始的点
这两个指针再一段无限长的环形路上,一定会相遇,而且,因为快的指针一定是从慢的指针后面“追”上来的,快指针至少比慢的指针多走了一个(n)环形
小学二年级的知识告诉我们,f 走的总距离是 s 的两倍,T相等,2Vs=Vf,时间相等,速度是两倍
得出2a+2b=a+b+k(b+c),f 的路程为a+b+转了k圈
导出a+b=k(b+c),
也就是说两点从 链表头 和 环形开始 点一同出发,速度都为1
那他们会在s和f相遇点相遇
这时我们把公式两端都去掉一个b
a=(k-1)b+c
两个点都少走b长,头出发的点去掉总路程的后端的b,sf相遇点出发的点去点前端b长,即 速度为1的两个点一个在表头出发走了a长的时候。会碰见在sf相遇点出发的另一个点
这两个点相遇点就是环形开始点
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
if (pHead == NULL)
return NULL;
ListNode* f, *s;
s = f = pHead;
do{
if (s->next == NULL || s->next->next == NULL)
{
return NULL;
}
s = s->next->next;
f = f->next;
} while (s != f);
f = pHead;
while (s != f)
{
f = f->next;
s = s->next;
}
return s;
}
};