142. 环形链表 II![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/bc47c3fa482e4135a3482110b274428e.png)
这道题既可以用哈希表做也可以用双指针做,用哈希表做比较简单,但是空间复杂度会比较高,这里就不写哈希的代码了。
直接看双指针怎么做。首先用双指针判断是否有环是比较容易的,让慢指针每次走一格,快指针每次走两格,如果相遇则说明有环,关键是要找到环的入口,这个涉及一点数学的知识。
这里直接用一下LeetCode的图:
在相遇时慢指针走过的路程为a + b(为什么直接在第一圈相遇待会再解释),快指针走过的路程为a + n * (b + c) + b,又因为快指针走过的路程是慢指针的两倍,所以2 * (a + b) = a + n * (b + c) + b,化简可得a = n * (b + c) - b,再化可得a = (n - 1) * (b + c) + c,n是大于等于1的(相遇时快指针至少走了一圈),所以这时头结点到环形入口的距离是相遇的结点到环形入口的距离加上整数倍的环形周长。这个时候我们再次使用双指针,一个指向头结点,一个指向相遇结点,两个节点同时向前走,一定会在环形入口相遇(因为从相遇结点向前走过n - 1次环再走c的距离和头结点走a的距离是相等的)。
所以代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if (head == nullptr)
return nullptr;
ListNode *slow = head;
ListNode *fast = head;
while (fast->next != nullptr && fast->next->next != nullptr) {
slow = slow->next;
fast = fast->next->next;
if (slow == fast) {
ListNode *index1 = head;
ListNode *index2 = slow;
while (index1 != index2) {
index1 = index1->next;
index2 = index2->next;
}
return index2;
}
}
return nullptr;
}
};
现在解释为什么慢指针在第一圈就会与快指针相遇,慢指针进入环后,我们以慢指针为参考系,则快指针的速度为一步一个节点,快指针离慢指针最远的情况为慢指针进入到环形入口,而快指针在入口的下一个节点,假设环有L个节点,此时以慢指针为参考系的话快指针需要走L - 1步,则不以慢指针为参考系的话慢指针也最多走L - 1步,仍在第一圈中(快指针不会跳过慢指针,因为相对速度是1)。