142. 环形链表 II(点击查看原题)
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。
说明:
不允许修改给定的链表。
进阶:
你是否可以使用 O(1) 空间解决此题?
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。
解题思路
这位大佬分析的很清楚,我也就直接摘抄过来作为这道题目的分析了,。
以下分析截取自:双指针技巧汇总
第一次相遇时,假设慢指针 slow 走了 k 步,那么快指针 fast 一定走了 2k 步,也就是说比 slow 多走了 k 步(也就是环的长度)。
设相遇点距环的起点的距离为 m,那么环的起点距头结点 head 的距离为 k - m,也就是说如果从 head 前进 k - m 步就能到达环起点。巧的是,如果从相遇点继续前进 k - m 步,也恰好到达环起点。
所以,只要我们把快慢指针中的任一个重新指向 head,然后两个指针同速前进,k - m 步后就会相遇,相遇之处就是环的起点了。
实现代码
struct ListNode *detectCycle(struct ListNode *head)
{
struct ListNode *fast,*slow;
fast = slow = head;
bool hasCycle =false;
while(fast!=NULL && fast->next!=NULL) //因fast每次走两步,若不判断fast->next是否为空,可能会出现当fast->next->next为空时,fast->next已经为空了的情况
{
fast = fast->next->next; //快指针每次走两步
slow = slow->next; //慢指针每次走一步
if(fast==slow)
{
hasCycle = true;
break;
}
}
if(hasCycle==false) //若==false,说明fast遇到了NULL,也就是链表没环
{
return NULL;
}
fast = head; //排除了链表没环的情况,就说明有环,把快指针指向head,重新向后走
while(fast!=slow)
{
fast = fast->next; //这次fast指针从head节点出发每次走一步
slow = slow->next; //slow指针从上次位置继续向后走,每次走一步
}
return slow; //当循环结束后说明fast与slow又相遇了,返回slow
}