描述
给定一个链表,如果链表中存在环,则返回到链表中环的起始节点,如果没有环,返回null。
样例
给出 -21->10->4->5, tail connects to node index 1,返回10
挑战
不使用额外的空间
思路
第一反应是用一个map<ListNode, bool> visited解决问题。一跑发现TLE。我感觉应该其实应该是因为内存限制。
第二反应:把经过的点统一地改成int infty 1 << 30。 结果成功了,打败了94.6%的提交。感觉是测试数据不够足。没有考虑超大测试数据的原因。
因为通过了就直接在网上搜了答案。然后打开了新世界的大门,解锁快慢指针。
快指针每次跳两步,慢指针每次跳一步。按照 快2慢1 快4慢2 ...的顺序进行。你可能会问问什么有环情况下两个指针一定会遇到呢?这是因为两个指针会遇到一定是 快指针跳的次数 - 慢指针跳的次数 = 环的长度。而按照快指针每次加2慢指针每次加1,快指针会比慢指针快1、2、3、4...所以一定能枚举出环的长度。
第二是如何找到起点呢?
这里可以知道 在遇到的地方是 F = Meet+Circle ,S = Meet。又有F = 2*S,则 Circle = Meet。 所以遇到的位置其实是这张图名义上的中点。 可以想象把整个环拉直,那么从起点每走一个Circle的长度就到了Meet的位置,每走一个Circle的长度就到了Meet的位置。因此Meet和Head是等价的,并且Meet和Head到Start的距离是相等的。
代码:
class Solution {
public:
/*
* @param head: The first node of linked list.
* @return: The node where the cycle begins. if there is no cycle, return null
*/
ListNode * detectCycle(ListNode * head) {
// write your code here
if(head == NULL || head->next == NULL)
{
return NULL;
}
ListNode* fast = head;
ListNode* slow = head;
while(fast != NULL && fast->next != NULL)
{
fast = fast->next->next;
slow = slow->next;
if(fast == slow)
{
break;
}
}
if(fast != NULL && fast == slow)
{
fast = head;
while(fast != slow)
{
fast = fast->next;
slow = slow->next;
}
return fast;
}
return NULL;
}
p.s: 注意特殊情况:head是NULL和head->next是NULL。