LeetCode Q141 Linked List Cycle
Given a linked list, determine if it has a cycle in it. To represent a cycle in the given linked list, we use an integer pos which represents the position (0-indexed) in the linked list where tail connects to.
If pos is -1, then there is no cycle in the linked list.
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
1思路 & 解法
1.1 思路一:每走过一步留下脚印👣,之后遇见脚印,输出有cycle;
代码如下:
bool hasCycle(struct ListNode *head) {
int impossibleNum = INT_MIN + 55467;
while (head != NULL) {
if (head->val == impossibleNum) {
return true;
}
head->val = impossibleNum;
head = head->next;
}
return false;
}
1.1.1 思路一扩展:显然上述代码有个弊端,修改了原链表;如何不修改原列表去记录脚印👣?
思路参考,用字典,用ListNode的address作为key,每走一个ListNode判断该node的address是否在字典内,如果链表走到底则没有cycle,否则则有cycle(且第一个遇到的为cycle的head)
由于c的标准库没有dictionary(reference),此处代码略
1.2 思路二:快慢指针,重叠以后证明有cycle
思路二解释:从head node开始,一个指针一次走一个node,另一个一次走两个node,,如果两者能够相遇,则证明有cycle
常识来想很简单,两个人在操场跑步,一个人跑的速度是另一个人的2倍,他们初始位置不限制;但慢的人跑一圈,快的会跑两圈,他们肯定会在某处相遇
证明:
问题可以等价为:对于cycle里任意两个node(节点),一个以1倍速同方向走,一个以2倍速同方向走,最终一定相遇;
设cycle长为l(l>0),k为两node初始距离(-l < k < l)
问题可视为 2x % l = (x + k) % l ,在 x > 0 情况下一定有解
即 2x = x + k + m * l (m可谓任意正整数) 在 x > 0 情况下有解
显然,
当k < 0时,令m = 1, 即x = l + k;为其中一个解,
当k >= 0 时,x = k, 为其中一个解;
代码:
bool hasCycle(struct ListNode *head) {
if (head == NULL) {
return false;
}
struct ListNode *pSlow = head;
struct ListNode *pFast = head;
while (pSlow->next != NULL && pFast->next != NULL && pFast->next->next != NULL) {
pSlow = pSlow->next;
pFast = pFast->next->next;
if (pSlow == pFast) {
return true;
}
}
return false;
}
LeetCode Q142 Linked List Cycle II
Given a linked list, return the node where the cycle begins. If there is no cycle, return null.
To represent a cycle in the given linked list, we use an integer pos which represents the position (0-indexed) in the linked list where tail connects to. If pos is -1, then there is no cycle in the linked list.
Note: Do not modify the linked list.
Follow-up: Can you solve it without using extra space?
思路 & 解法
考虑到Do not modify the linked list和Can you solve it without using extra space?,q141思路一不适用,考虑q141思路二上做文章;
但是,如何拿到cycle的headnode?
设cycle前node集为pre,pre长度为p,cycle长为l,相遇节点为cycle的第k个node,相遇时慢指针pSlow走的步数为total。
则有:
①2total = p + k + m * l (m ∈ Z)
②total = p + k + n * l (n < m, n 属于 Z)
① - ②得 ③total = (m - n) * l
可得p + k = (m - 2n) * l
p + k 是 l的倍数,如何使用该条件得到loopHead?
我们知道,当我们快慢指针相遇时,我们当前的节点位置为k,那么让慢指针k再走p个节点,一定为loopHead节点。
但如何走一个p节点?
brilliant idea:再让一个慢指针从head节点走,两指针第一次相遇,则两者走了p节点!且该节点就是要求的cycle的headnode!!!
参考图:
struct ListNode *detectCycle(struct ListNode *head) {
if (head == NULL) {
return head;
}
struct ListNode *pSlow = head;
struct ListNode *pFast = head;
bool isCycle = false;
while (pSlow->next != NULL && pFast->next != NULL && pFast->next->next != NULL) {
pSlow = pSlow->next;
pFast = pFast->next->next;
if (pSlow == pFast) {
isCycle = true;
break;
}
}
if (isCycle) {
pFast = head;
while (pFast != pSlow) {
pFast = pFast->next;
pSlow = pSlow->next;
}
return pFast;
}
return NULL;
}