目录
4.1 为什么slow和fast一定会在环中相遇?会不会在环里面错过,永远遇不上?请证明一下:
4.2 为什么你要让slow每次走一步,fast每次走两步呢? 能不能fast一次走n步(n>2)?请证明一下?
4.3 slow走1步,fast走2步,一定会相遇。如何求环的入口点呢?
1.典型的单链表中环形链表的题
给你一个链表的头节点 head
,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos
不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true
。 否则,返回 false
。
2.解题思路
1. 定义两个指针fast和slow。
2. fast快指针一次走两步,slow慢指针一次走一步。
3. 不带环,fast就会为空。
4. 带环,fast就会在环里面追上slow。
3.代码实现:
struct ListNode {
int val;
struct ListNode *next;
};
bool hasCycle(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)
{
return true;
}
}
return false;
}
4.延伸问题
4.1 为什么slow和fast一定会在环中相遇?会不会在环里面错过,永远遇不上?请证明一下:
证明:
第一步: slow和fast,fast一定是先进环的,这时slow走了入环前距离的一半。
第二步: 随着slow进环,fast已经在环里面走了一段,走了多少跟环的大小有关。
假设slow进环的时候,slow根fast的距离是N。
第三步: fast开始追slow ,fast每次向前走两步,slow每次向前走一步,每追一次,判断一下是否相遇? 每追一次, fast和slow的距离变化为:N,N-1,N-2,……,0 。
可以发现每追一次,距离减少1,他们之间的距离最后减到0,就是相遇点,所以一定会相遇。
4.2 为什么你要让slow每次走一步,fast每次走两步呢? 能不能fast一次走n步(n>2)?请证明一下?
证明:
假设: slow一次走1步,fast一次走3步的情况。slow进环以后,fast跟slow 之间的距离为N,fast开始追slow。他们之间的距离变化如下:
N是偶数 | N是奇数 |
N | N |
N-2 | N-2 |
N-4 | N-4 |
... | ... |
2 | 1 |
0 | -1 |
如果N是奇数,距离变成-1意味着他们之间的距离变成了C-1(C是环的长度)。
此时,如果C-1 是奇数,那么就永远追不上了。如果C-1是偶数。那么就可以追上。
综上: 以此类推可以看出当n>2时,有可能出现追不上的问题。所以选择让slow每次走一步,fast每次走两步。
4.3 slow走1步,fast走2步,一定会相遇。如何求环的入口点呢?
给定一个链表的头节点 head
,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos
是 -1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。不允许修改 链表。
思路图:
证明:
追上相遇的过程中:
慢指针走的距离: (因为X是小于C的,慢指针走一圈,快指针能走两圈,期间一定可以相遇了。所以是X,而不是N*X)
快指针走到距离: N是他们相遇之前,fast在环里走的圈数。
已知快指针走的路程是满指针的2倍:
综上:可以看出一个指针从meetNode回到入口处的距离等于另一个指针从头走到入口点处的距离。用一个指针从meetNode走,同时一个指针从头开始走,它俩相遇点就是入口点。
代码实现:
struct ListNode {
int val;
struct ListNode *next;
};
bool hasCycle(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 *meetNode = slow;
// 公式证明的
while(meetNode != head)
{
meetNode = meetNode->next;
head = head->next;
}
return meetNode;
}
}
return NULL;
}
最后,这是我在学习单链表中的环形链表时的笔记,如果有错误的地方希望大家指正。大家一起努力!