快慢指针寻找环入口——学习笔记

快慢指针常用于判断链表中是否有环和寻找链表中值。

快慢指针的原理在于一快一慢两个指针,快指针每次移动两个节点,慢指针每次移动一个节点。

如何判断链表有环?

假如链表没环,那么快指针必然会先遇到空指针,即可判断链表无环。

而当链表有环时,快慢指针必然在环内循环,那么此时快慢指针就像钟表上的时针和分针,不管怎么转总有一刻会重合在一起。

可能有同学还不是特别明白。

首先有个长度为n的环,假设此时快指针在x1处,慢指针在x2处。

环
-------->x1---------->---
^                       |
|                       | 
|                       |       
---<-----------x2<-------

此时快指针距离慢指针 d=x1+n-x2

快指针每次移动2,慢指针每次移动1,所以快指针相对慢指针移动1,那么只需要移动 d 次快指针就能追上慢指针了。

如果还不清楚就想想钟表吧~看代码:

struct Node{
    int val;
    Node *next;
};

//判断链表是否有环
bool hasCircle(Node *node) {
    
    //定义快慢指针
    Node *fast=node;
    Node *slow=node;
    
    //因为快指针要走两个节点,所以要多判断一步,遇到空指针说明没有环
    if (fast != NULL && fast->next != NULL) {
        
        fast = fast->next->next;
        slow = slow->next;
        
        //当快指针等于慢指针时,说明快指针绕环行进赶上慢指针了,证明有环
        if (fast == slow) {
            return true;
        }
    }
    
    return false;
}

如何寻找环入口?

我们先假设链表长度为N,环长度为n,那么我们就可以直接计算出快慢指针的相遇节点:

一、当n>=N/2时,快慢指针相遇第n个节点

证明,可推理出此时慢指针必然在环内,慢指针走了n步时,快指针走了2n步,比慢指针多走了n步,正好绕环一周回到 n节点,故此时相遇。

二、当n<N/2时,快慢指针相遇在 第(N-N%n)个节点

证明,由于 N%n<n,故此时慢指针已在环内,快指针比慢指针多走了(N-N%n)步,而(N-N%n)必然是 n的倍数,故 快指针 只比慢指针多绕环走了几整圈而已,此时快慢指针相遇。

那么我们说了上面这么多有什么用呢?我们实际上并不知道N和n的值为多少,但是我们通过判断有环的操作时实际上已经找到了快慢指针的相遇节点,也就是刚刚所说的 n节点或者(N-N%n)节点,我们简单点就称之为 m节点吧。

由刚才的分析可知m节点有个特点,那就是在m节点往下接着走m步最终还是回到m节点。那么我们利用这个特性,让快指针回到起点,慢指针仍然待在m节点,两个指针同时开始走,每次都只走一步。因为两个指针走m步后都会走到m节点,那么当它们第一次相遇时就必然是在环的起点。

还是比较好理解的是吧。哈哈看代码:

Node *getCircleHead(Node *node){
    //先判读是否有环
    if (hasCircle(node)) {
        
        Node *fast=node;
        Node *slow=node;
        
        //找到交点m
        do{
            fast=fast->next->next;
            slow=slow->next;
        }while (fast!=slow);
        
        //找环入口
        fast=node;
        while (fast!=slow) {
            fast=fast->next;
            slow=slow->next;
        }
        return fast;
    }
    return NULL;
}

猜你喜欢

转载自blog.csdn.net/jjwwwww/article/details/82228174