目录
1.环形链表
题目:给你一个链表的头节点 head ,判断链表中是否有环。如果链表中存在环 ,则返回 true 。 否则,返回 false 。
链接:力扣
方法:用快慢指针
- slow一次走一步,fast一次走两步。
- fast先走到空说明没环。fast和slow相遇就说明有环(带环不会走到空)。
为什么链表有环,两指针就一定会相遇?
相当于一个追及问题。因为他们俩的距离每次减少1,slow进环之后,fast一定会追上slow。
slow进环时:fast什么情况?fast可能走了不到一圈。也可能已经走了n圈(因为环小)。
带环的三种情况
1.指向头
2.指向中间
3.指向自己
bool hasCycle(struct ListNode *head) {
struct ListNode * slow=head,* fast=head;
while(fast && fast->next)//fast走到空,说明没环,跳出循环。没环的话:偶数个结点 fast为空走完,奇数个fast->next为空走完
{
slow=slow->next;//先走再判断是不是相等,因为刚开始都指向head
fast=fast->next->next;
if(fast==slow)//相遇
{
return true;
}
}
return false;
}
没环的特别情况
while(fast && fast->next) //循环条件这样设置同时也处理了这种情况。因为fast==head ;head->next==NULL; 根本就不会进入循环
2.环形链表||
题目:给定一个链表的头节点
head
,返回链表开始入环的第一个节点。 如果链表无环,则返回null
。链接:力扣
快慢指针寻找相遇结点 slow每次走一步,fast每次走两步。slow入环时,fast可能走了不到一圈。也可能已经走了n圈(因为环小)。
设环的周长为C,slow进环前的一段长L,环的入口点到快慢指针相遇点长X。
slow走的路程:L+X
fast走的路程:L+n*C+X
进行一个公式推导:(slow路程)*2=fast的路程
2*(L+X)=L+n*C+X
L=n*C-X
L=n*(C-1)+(C-X)
寻找入环的第一个结点
一个指针指向head,一个指针指向meet,两个指针同时走,每次一步。
L就是head走的路程,n*(C-1)+(C-X) 就是meet走的路程。路程相等,所以两个指针一定会相遇。
这两个指针的相遇结点就是我们想要的入环第一个结点(因为head指针走了L步)。
欣赏一个粗制滥造的动图🐱
完整代码:
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode * slow,*fast;
slow=fast=head;
while(fast && fast->next)//有环就会一直循环
{
slow=slow->next;
fast=fast->next->next;
if(slow==fast)//相遇了
{
struct ListNode * meet=slow;//创建一个meet指针,指向快慢指针的相遇结点。开始找入环的第一个结点。
while(meet !=head)
{
meet=meet->next;//两个指针都是一次一步,根据公式我们知道这俩指针迟早会相遇。
head=head->next;
}
return head;//这个相遇结点就是我们要的入环第一个结点。
}
}
return NULL;
}