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 *fast = pHead;
ListNode *slow = pHead;
ListNode *meet = NULL;
int flag = 0;
while (fast->next)
{
slow = slow->next;
fast = fast->next->next;
if (fast == slow)
{
flag = 1;
meet = fast;
break;
}
}
if (flag == 1)//说明有环
{
slow = pHead;
while (slow != meet)
{
slow = slow->next;
meet = meet->next;
}
return meet;
}
else
{
return NULL;
}
}
};
题目描述
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
因为之前接触过带环的链表所以写这道题的时候直接想到了快慢指针,程序放到牛客网上也是直接通过了,但是在看其他大神提交的代码的时候,感觉自己写的代码还是很混乱,多了很多其他不需要的东西。
https://blog.csdn.net/Hanani_Jia/article/details/79810799 之前的博客已经讲解过一次带环链表去求入口点和环的长度了,这里我们再简单的说一下。
简单来说我们平时的单链表是只有黑色部分的,链表的最后一个结点的next指针是指向空位置,但是带环的不一样,最后一个节点的next指针指向了之前的一个节点,通俗点来说这种链表是没有尾巴的没有最后一个结点,所以这种链表如果用while循环来遍历的话会是一个死循环,进去就出不来了,我们就是用这个特点来去算他是不是带环的链表,那也就是说我们需要用while循环来实现我们的程序,但是我们怎么知道我们的while循环是不是结束了,通过程序执行时间?还是通过程序崩溃...这都不是很好的办法,所以我们就用了快慢指针,就是有两个指针一个指针快一个指针慢,假如我们现在两个指针都从头结点开始出发,快指针一次走两个节点也就是fast = fast->next->next;慢指针一次走一个slow = slow->next;如果说这个链表没有环,那就是当你的fast指针是空了,说明没有环,但是什么情况说明有环?因为每次两个指针走的结点数是不一样的,所以如果是单链表的话两个指针是不可能相遇的,但是如果说这个链表存在环,那最终就是两个指针都在环里移动,一个一次动一个一个一次动两个这样两个指针总是会相遇的,这里有个问题就是快指针能不能一次移动三个、四个或者更多,我上边的博客有答案大家可以去看。所以说如果两个指针相遇那就说明链表有环,这个还是相对容易理解的,那我们看我们怎么去找我们链表的入口位置。
我们通过一张图来解释,图里边黑色加红色是我们的链表,红色是链表里边环的部分,链表中环的长度也就是红色部分的长度是C。
这里还是要明白一个问题,就是X一定是小于等于C的也就是说慢指针在环里边最多走了一圈两个指针就相遇了,因为两个指针离的最远的情况就是慢指针进来,快指针刚走了环的一半这时候慢指针走半圈的时候到当前快指针的位置,快指针走的路程是慢指针的两倍,那快指针就走了一圈又回到现在这位置了,所以这是最远的情况所以X不可能大于C。
明白了这个问题就好办了那就能得到一个式子
所以说这里还要说一个应该注意的点就是快慢指针一定要从同一个位置开始走。对这个式子化简一下
L+nC+X=2L+2X---->nC=X+L-->L=NC-X,
橙色部分是我们的C-X,n是我们又转了多少圈,所以我们现在让一个指针从相遇位置开始走,然后让另一个指针从头开始走,当从头开始走的那个指针走到环入口点的时候也就是说走了L的长度,另一个指针走了nC-X,也是到了入口点,也就是两个指针相遇的地方就是我们的环的入口点。
如果说这个题再加一个求环的长度,其实更简单,我们让一个指针在meet位置等着让另一个指针开始走,每走一次计数器加一个一两个指针相遇的时候计数器就是我们环的长度。