原题传送门
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。
输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。
本题的两个主要思考点是:
1.判断链表是否有环
2.如果有环,那如何找到这个环的入口
慢指针:x + y 每次移动一步
快指针:x + y + n(y + z) 每次移动两步
2(x + y) = x + y + n(y + z)
x + y = n(y + z)
x = n(y + z) - y
x = (n - 1) (y + z) + z
x = z
index1 = fast
index2 = head
第二重双指针
这道题重点在于思路,知道你思路清楚了,代码是很好写的
ListNode* fast = head;
ListNode* slow = head;
while(fast != NULL && fast->next != NULL){
slow = slow->next; //慢指针每次移动一步
fast = fast->next->next; //快指针每次移动两步
if(fast == slow){ //当两指针在环中相遇时
ListNode* index1 = fast; //定义一个结点指向相遇的结点
ListNode* index2 = head; //定义一个结点指向头结点
//继续遍历,直到两结点都运动到环形入口的起始位置
while(index1 != index2){
index1 = index1->next;
index2 = index2->next;
}
return index1;
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* fast = head;
ListNode* slow = head;
//因为快指针需移动两步,因为要判断两个位置
while(fast != NULL && fast->next != NULL){
slow = slow->next; //慢指针每次移动一步
fast = fast->next->next; //快指针每次移动两步
if(fast == slow){ //当两指针在环中相遇时
ListNode* index1 = fast; //定义一个结点指向相遇的结点
ListNode* index2 = head; //定义一个结点指向头结点
//继续遍历,直到两结点都运动到环形入口的起始位置
while(index1 != index2){
index1 = index1->next;
index2 = index2->next;
}
return index1;
}
}
return NULL;
}
};
n 其实不一定为1,也可以转20圈,转50圈,最后在第二重双指针时,index1可能也会在圈中传转了一圈又一圈,index2才到达环形入口处,所以你n为50,为100都是一样的,这里设置为1,只是看得时候清晰方便一些,可以很快地得出x和z之间的关系,然后去写出代码逻辑
因为快指针一定是先进入环内的,然后慢指针才进到环内,然后当慢指针进入下一个入口时,快指针走的一定是慢指针的两倍,所以慢指针在没有进入到下一个入口处时,快指针在中间的某个位置一定和其相遇了
证明如下:
在快指针fast进入环口3时,它已经走了k + n个结点,从图中可以清晰地看出,k为快指针和慢指针之间的距离,n为一个环的距离,而慢指针在进入环内相应地走了(k + n)/2个结点,从图中可以看出k是小于n的,所以(k + n)/2也一样是小于n的,即慢指针在进入环内一圈不到的距离就会和快指针相遇
所以慢指针走动的距离为x + y就够了,其不会再走第二圈
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
unordered_set<ListNode*> visited;
while(head != NULL){
if(visited.count(head)) //若在哈希表中发现有遍历过的结点
return head; //则返回此环的起始结点
visited.insert(head); //若在哈希表中没找到,则将其插入表中
head = head->next; //头结点继续向后移动
}
return NULL; //若遍历到为空,则说明链表一定无环
}
};
看了本文,您对环形链表的理解有没有更加深入了呢,如果还觉得有些模糊,则应该配合动画和原理图的分析再去推理一遍,这题在力扣中虽然属于中等题,但是双指针的这种解法还是比较难理解的,主要还在于数学的推理分析过程
OK,本次的讲解就到这里,感谢您对于本文的观看,如果疑惑请于评论区留言或私信给我