给你一个链表的头节点 head
,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos
不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true
。 否则,返回 false
示例 1:
输入:head = [3,2,0,-4], pos = 1 输出:true 解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0 输出:true 解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1 输出:false 解释:链表中没有环。
一、方法:快慢指针
- 首先检查链表是否为空或只有一个节点,如果是,则肯定没有环。
- 初始化慢指针 slow 指向头节点,快指针 fast 指向头节点的下一个节点。
- 进入循环,如果快指针或快指针的下一个节点为空,则链表中没有环。
- 否则,慢指针每次移动一步,快指针每次移动两步,直到两者相遇或快指针到达链表尾部
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
bool hasCycle(struct ListNode *head) {
if (head == NULL || head->next == NULL) {
return false;
}
struct ListNode*slow = head,*fast = head;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
if(slow == fast)
{
return true;
}
}
return false;
}
二、为什么一定会相遇,有没有可能错过,永远追不上?请证明
首要条件是,链表一定是带环的,才有讨论的必要。
假设slow进环的时候,fast和slow的距离是N。
fast追击slow的过程中距离变化如下:
N N-1 N-2 .... 2 1 0 (每追击一次,距离缩小1,距离为0就追上了)
因为N一直减1,肯定会减到0,得证一定会相遇。
三、slow一次走1步,fast走3步,可不可以相遇?请证明
证明fast走3步:
slow走一步,fast走三步(fast = 3slow)。slow进环的时候,fast在环里面走了很长一段距离了。
假设slow进环的时候,fast和slow的距离是N。
fast追击slow的过程中距离变化如下:会有两种可能性
N是偶数:N N-2 N-4 .... 4 2 0 (每追击一次,距离缩小2,距离为0就追上了)
N是奇数:N N-2 N-4 .... 3 1 -1
N是偶数,一直追击,是可以追到距离为0的,但是如果N是奇数,追击到是-1就是fast和slow错过了,又得进行新一轮追击,距离变成C-1(假设C是环的长度)。
这个时候,如果C-1是偶数,可以追上,但是如果C-1是奇数,进入死循环(永远错过)。
总结:
1- N是偶数,第一轮就追上了。
2- N是奇数,第一轮追击会错过,fast和slow距离变成C-1
a- 如果C-1是偶数,下一轮追上了
b- 如果C-1是奇数,那么就永远追不上
如果N是奇数,且C是偶数,那么就永远追不上。
证明:是否同时存在N是奇数且C是偶数 。
假设slow进环时,fast跟slow距离是N,fast在环里面已经转了x圈。
- slow走的距离是:L
- fast走的距离是:L+x*C+C-N
- fast走的距离是slow的3倍:3*L = L+x*C+C-N
- 化:2*L=(x+1)*C-N
- 是否存在C是偶数,N是奇数。2*L=(x+1)*C-N 是否符合该表达式 偶数=(x+1)*偶数 - 奇数
- 上面的表达式 偶数=(x+1)*偶数 - 奇数 肯定不存在,只有奇数-奇数才等于偶数
所以得证N是偶数,C是奇数,不能同时存在,永远追不上的条件不成立。