leetcode141.
思路: 快慢指针,即慢指针一次走一步,快指针一次走两步,两个指针从链表起始位置同时开始运行,如果链表带环则一定会在链表中相遇,否则快指针率先走到链表末尾。
代码如下
bool hasCycle(struct ListNode *head) {
struct ListNode*slow=head,*fast=head;
//fast分为奇数个和偶数个
while(fast&&fast->next)
{
slow=slow->next;
fast=fast->next->next;
if(slow==fast)
return true;
}
return false;
}
【扩展问题】
为什么快指针每次走两步,慢指针走一步可以?
假设链表带环,两个指针最后都会进入环,快指针先进环,慢指针后进环。当慢指针刚 进环时,可能就和快指针相遇了,最差情况下两个指针之间的距离刚好就是环的长度。 此时,两个指针每移动一次,之间的距离就缩小一步,,因此:在满指针走到一圈之前,快指针肯定是可以追上慢指针的,即相遇。
假设slow进环时,fast和slow之间的距离为N
slow进环后,fast和slow的距离变化,每次追击距离缩小1.
快指针一次走三步,走四步,.....n步行吗?
我们这里假设slow一次走一步,fast一次走三步
当slow走到一定的时候fast已经入圈了。
假设slow进环时,fast和slow之间的距离为N
slow进环后,fast和slow的距离变化,每次追击距离缩小2
1.N是偶数下一圈直接相遇。
2.N是奇数,N为奇数遇到刚好错过
距离为-1代表,新一轮的开始,这个距离C-1(假设C是环的长度)
1.C-1是奇数,继续追击,陷入循环。
不成立
2.C-1是偶数,下一圈直接相遇。
总结:
1.N如果是偶数直接追上。
2.如果N是奇数,C是奇数,第一轮错过,但第二轮追上
3.如果N是奇数,C是偶数,就永远追不上。
不成立
所以一定能追上。
证明:
假设slow进环时,fast和slow之间的距离为N
起点到环入口点:L
slow入环的时候,fast-slow的距离是N
环的长度为c
slow入环的时候慢指针走的路程:L
快指针走的路程:L+n*C-N(n>=1)
快指针走的路程是慢指针的3倍
3L=L+n*C-N
2L=n*C-N
如果N是奇数,C是偶数,就永远追不上
2L=n*C-N
偶数=n*偶数-奇数:不成立
leetcode142.
结论: 让一个指针从链表起始位置开始遍历链表,同时让一个指针从判环时相遇点的位置开始绕环 运行,两个指针都是每次均走一步,最终肯定会在入口点的位置相遇。
1. 假设慢指针走一步,快指针走两步。
起点到环入口点:L
入口点到相遇点:X
环的长度:C
从开始相遇时slow走的距离:L+X
从开始相遇时fast走的距离:L+n*C+X(slow进环前,fast已经在环里面转了n圈)
fast路程=slow路程*2
2*(L+X)=L+n*C+x
L+X==n*C
L=n*C-X
结论:一个指针从相遇点开始走,一个指针从头开始走,他们会在入口点相遇
快慢指针求出相遇点,然后,一个指针从相遇点开始走,一个指针从头开始走,走到循环入口点代码如下:
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode*slow=head,*fast=head;
while(fast&&fast->next)
{
slow=slow->next;
fast=fast->next->next;
if(slow==fast)
{
//相遇点
struct ListNode*meet=slow;
while(head!=meet)
{
//同时走
head=head->next;
meet=meet->next;
}
return head;
}
}
return NULL;
}