OJ题1:https://leetcode.cn/problems/linked-list-cycle/ //判断链表是否有环
2:力扣 //返回链表开始入环的第一个节点
环形链表示例:
解决方法:
Slow和fast指向链表的开始
Slow一次走一步
Fast一次走两步
不带环,fast就会为空
带环,fast就会在环里面追上slow
问题:
- 为什么slow和fast一定会在环中相遇?会不会在环里面错过,永远追不上?请证明。
- 为什么slow走一步,fast走两步呢?能不能fast一次走n步?(n>2)?请证明。
问题1:
第一步:slow和fast,fast一定先进环,这时slow走了入环前距离的一半
第二步:随着slow进环,fast已经在环里面走了一段,走了多少跟环的大小有关系
假设slow进环的时候,slow和fast的距离是N,fast开始追slow
每追一次,fast和slow的距离变化:
N
N-1
N-2
N-3
……
1
0
Slow每次走一步,fast每次走两步:
每追一次,距离较2,他们之间的距离最后减到0的时候就是相遇点。
假设slow进环的时候,slow和fast的距离是N,fast开始追slow
每追一次,fast和slow的距离变化:
N------N为偶数 N--------N为奇数
N-2 N-2
N-4 N-4
N-6 N-6
…… ……
2 1
0 -1
可以追上 这一次追不上
N是奇数,距离变为-1,什么意思?意味着他们之间的距离变成了C-1(C是环的长度);如果C-1是奇数,那就永远追不上了;如果C-1是偶数,那么就可以追上。
问题:Slow走一步,fast走两步,一定会相遇,如何求环的入口点呢?
法一:结论:一个指针从相遇点开始走,一个指针从链表头开始走,他们会在环的入口处相遇。
证明:
在追上相遇的过程中,
慢指针走的距离:L+X
快指针走的距离:L+N*C+X (N≥1) N是相遇前,fast在环里走的圈数
快指针走的距离是慢指针的1倍
2*(L+X)=L+N*C+X
L+X=N*C
L=N*C-X
L=(N-1)*C+C-X
其中,(N-1)*C表示从meet回到meet,C-X代表剩下的路程。
代码实现:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode *detectCycle(struct ListNode *head)
{
struct ListNode *slow=head;
struct ListNode *fast=head;
while(fast&&fast->next)
{
slow=slow->next;
fast=fast->next->next;
if(slow==fast)
{
struct ListNode *mid=slow;
struct ListNode *cur=head;
while(mid!=cur)
{
cur=cur->next;
mid=mid->next;
}
return cur;
}
}
return NULL;
}
法二:设相遇点的下一个点为newHead,把相遇点和下一个点断开,让相遇点指向NULL,newHead作为新链表的头,此时就被转化为相交链表的问题。
代码实现:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode *find(struct ListNode *headA, struct ListNode *headB)
{
struct ListNode *curA=headA;
struct ListNode *curB=headB;
int A=1;
int B=1;
while(curA->next)
{
curA=curA->next;
A++;
}
while(curB->next)
{
curB=curB->next;
B++;
}
if(curA==curB)
{
int gap=abs(A-B);
struct ListNode *fastList=headA;
struct ListNode *slowList=headB;
if(A<B)
{
fastList=headB;
slowList=headA;
}
while(gap--)
{
fastList=fastList->next;
}
while(fastList!=slowList)
{
fastList=fastList->next;
slowList=slowList->next;
}
return slowList;
}
else
{
return NULL;
}
}
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode *slow=head;
struct ListNode *fast=head;
while(fast&&fast->next)
{
slow=slow->next;
fast=fast->next->next;
if(slow==fast)
{
struct ListNode *list=slow->next;
slow->next=NULL;
struct ListNode *cur=find(list,head);//相交链表找交点
return cur;
}
}
return NULL;
}
法二改进:心里想的是要断开,实际实现可以不用断开,以meet作为终点也可以实现;适合题目中有要求不能断开链表