[142. 环形链表 II]
「这是我参与2022首次更文挑战的第22天,活动详情查看:2022首次更文挑战」。
题目描述
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
示例
示例1 :
输入: head = [1,2,3,4,5], n = 2
输出: [1,2,3,5]
示例 2:
输入: head = [1], n = 1
输出: []
示例 3:
输入: head = [1,2], n = 1
输出: [1]
提示:
- 链表中节点的数目范围是 [0, 104]
- -105 <= Node.val <= 105
- pos 为 -1 或者链表中的一个 有效索引 。 进阶: 你能用 O(1)(即,常量)内存解决此问题吗?
思路
在之前的文章里面,我们已经能够判断链表里面是否有环了,那么怎么找环的入口呢,即跑道的入口?之前的说过的哈希表,第一个相等的就是环的入口,那么双指针得怎么修改呢?按照之前的写法,我们可以得到在环内相遇的节点。下来又到了数学上的追及问题了,我们还是假设从一段直道跑到环形跑道的跑道上,我们设直道为x,从环道入口到相遇点为y,相遇点到环形入口为z。在环里相遇时,slow指针走了 x+y ,fast走了 x+y+n(y+z) ,n为fast跑的圈数。且至少为一,并且x+y是已知的。又因为fast的速度为slow的2倍,所以有2(x+y)=x+y+n(y+z),我们求环道入口其实就是离起点x的地方,也是离相遇点z的地方,所以,我们从起点走x,或者走x+n(y+z) (n>=1),都会到相遇的地点。所以于我们整理得 x = n (y + z) - y,不妨假设 n=1 ,那么可以到 x=z 的结论,当 n>1 时,将x = n (y + z) - y 代入 x+n(y+z) 可知走(2n-1)(y+z)+z 可到达环道入口。总上,从起点走z,走(2n-1)(y+z)+z,都可以到达入口。
代码实现
求离起点距离为z的节点,且x+y也已知,而且从x+y在走z,即走x+y+z 也会是到达相遇点。可以以此作为编码的基础。走(2n-1)(y+z)+z,在链表上的实际距离恰好也是走z的距离。怎么走可以参考
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode* head) {
ListNode* slow = head;
ListNode* fast = head;
while (fast!= nullptr &&fast->next!=nullptr) {
slow = slow->next;
fast = fast->next->next; //找x+y
if(slow==fast){
ListNode* FormMeet=fast; //记录x+y
ListNode* FromHead=head; //开始走z
while(FormMeet!=FromHead)
{
FormMeet=FormMeet->next;
FromHead=FromHead->next;
}
return FormMeet;
}
}
return nullptr;
}
};
总结
在链表上使用双指针,最最重要的一点是要挖掘出双指针的指向需要满足什么样的条件,才能符合我的要求。这道题针对了双指针的速度进行了讨论。