LeetCode-环形链表、环形链表 II

一、环形链表

. - 力扣(LeetCode)

判断是否有环,使用快慢指针,开始时都指向头节点,快指针每次走两部,慢指针每次走一步,如果在走的过程中,慢指针和快指针相同(也就是快指针和慢指针指向的节点的同)那么就说明这个链表是带环链表;

原理: 

若是这个链表代换,那么快慢指针一定不会走向NULL;只会在这个链表里面循环走下去,并且当循环条件允许时都会进环;同时快指针走的每次都比慢指针多一步,若是一直走下去 ,虽说快指针开始一直在慢指针前面,但是每次快指针都多走一步,最后快指针一定会和慢指针相遇,(就像跑步速度相异的两人在环形跑道上面跑步同理,开始时快的再前面,可能一圈或是两圈之后,跑的快的一定会和跑的慢的相遇)

因此,我们可以把快慢指针相遇作为判断链表是否带环的判断准则,当快慢指针相遇,就说这个链表带环;

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode ListNode;
bool hasCycle(struct ListNode *head) {
    
    ListNode* fast=head,*slow=head;

    while(fast && fast->next && slow)
    {
        fast=fast->next->next;
        slow=slow->next;
        
        if(fast==slow)
        {
            return true;
        }
    }
    return false;
}

当头节点为NULL,或者是头节点只有一个且这个节点的next为NULL时,不进入循环,直接返回false,可见这种情况也能兼容。

当fast每次走三步或者是更多步呢?(题外话)

先看快指针每次走三步的情况:假设有环,经过上面解法可知,快慢指针相遇一定在环内部;所以我们研究在环中的过程,当慢指针进环的时候快指针已经在换里面走了一会了,我们并不清楚它走了几圈或是多远;假设环可以容纳的节点个数是C(环的长度),假设慢指针刚刚进入环的时候距离环内快指针的距离是N,那么每走一步快慢指针之间的距离就减少2,若是N是偶数,那么快慢指针就能相遇;

若是N是奇数,那么快指针在接近慢指针时相遇的距离是奇数,但是两者每次距离减少偶数2,所以快指针会追上慢指针,并且多出慢指针一个节点的距离;进入第二轮,在环中此时两者之间的距离是(C-1);若是(C-1)是偶数,那么快慢指针最终会相遇;若是(C-1)是奇数,那么会出现和第一轮相同的情况:快指针多出慢指针一个节点的距离,此时两者之间的距离又变成了(C-1);而已知(C-1)是奇数,后面的都是同样如此,这样看来似乎快慢指针永远都不会相遇;

但其实不然:

上面快慢指针不会相遇的情况出现在该条件之下:当N是奇数的时候,C是偶数;这种条件真的存在吗?假设头节点距离进环节点的距离是L,研究慢指针刚刚进环的时刻;此时慢指针走了L,假设快指针在环内走了x圈,那么快指针走的总路程就是L+x*C+N(N是慢指针刚刚进入环的时候与快指针之间的距离),同时又因为快指针走的距离是慢指针的三倍,那么就有以下等式

3*L=L+x*C+N ;

2*L=x*C+N

带入快慢指针永远不会相遇的条件:N是奇数的时候,C是偶数;

因为2*L永远是偶数,而条件带入之后,等式右边无论x是几,等式右边的最终结果都是奇数

由此可知,这种条件不会存在;当快指针每次走的步数更多得到时候,也同样如此;也就是说快慢指针一定会相遇。 


二、环形链表 II

. - 力扣(LeetCode)

 本题思路:首先判断是否带环,若是不带环就返回NULL;若是带环则返回开始进环时的那个节点指针;判断带环与否参照上一题环形链表;而对于返回进环节点指针,使用下述方法:在判断完带环与否之后我们可以得知快慢指针相遇的节点,记录下这个相遇节点,设置一次循环,让头节点从头开始遍历,相遇节点从相遇的节点开始遍历,若是两个节点相同,那么这两个节点相遇时的节点就是进环时的节点;

原理:

假设进环时的节点和头节点之间的距离是L,fast和slow指针相遇的节点距离进环节点之间的距离是N,,快慢指针相遇之后快指针在环中走了x圈,环的长度是C,把快慢指针相遇的节点称为相遇节点;找等式关系,快指针每次走两步,慢指针每次走一步,快慢指针相遇之后,快指针走的距离是慢指针的两倍,慢指针走了L+N,快指针走了L+x*C+N ;那么就有

2*(L+N)=L+x*C+N;

L+N=x*C;

L=x*C-N;

L=(x-1)*C+C-N;

 得出这个关系就说明这种方法可以找到进环节点,解释:L是头节点距离进环节点的距离,头节点从头走起,相遇节点从快慢指针相遇的节点走起,两者每次都走一步,头节点走L时,相遇节点也走了L,也就是(x-1)*C+C-N;不用管(x-1)*C,因为走完(x-1)*C之后回到相遇节点,重点是C-N,我们已知相遇节点和进环节点之间的距离是N,这时已经走过的,未走过的是C-N,那么相遇节点的最后相当于走了C-N,刚好到进环节点;

所以可以利用头节点和相遇节点一起开始遍历、找出两者相同的节点的方法得出进环节点;

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode ListNode;
struct ListNode *detectCycle(struct ListNode *head) {
    ListNode* fast=head,*slow=head;
    int flag=-1;
    
    while(fast && fast->next && slow)
    {
        slow=slow->next;
        fast=fast->next->next;
        if(fast==slow)
       {
            flag=1;
            break;
       }
    }
    if(flag==-1)
        return NULL;

    while(head!=slow)
    {
        head=head->next;
        slow=slow->next;
    } 
    return head;
}

相关推荐

  1. LeetCode热题100】【环形 II

    2024-07-21 12:38:02       35 阅读
  2. leetcode142.环形II

    2024-07-21 12:38:02       64 阅读
  3. leetcode-环形

    2024-07-21 12:38:02       57 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-21 12:38:02       52 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-21 12:38:02       54 阅读
  3. 在Django里面运行非项目文件

    2024-07-21 12:38:02       45 阅读
  4. Python语言-面向对象

    2024-07-21 12:38:02       55 阅读

热门阅读

  1. 如何使用C++中的字符串类(如std::string)

    2024-07-21 12:38:02       15 阅读
  2. Ubuntu 20安装JDK17和MySQL8.0

    2024-07-21 12:38:02       17 阅读
  3. OpenJudge | 约瑟夫问题

    2024-07-21 12:38:02       15 阅读
  4. 在Jupyter Notebook中进行大数据分析:集成Apache Spark

    2024-07-21 12:38:02       16 阅读
  5. webpack

    2024-07-21 12:38:02       21 阅读
  6. 算法剩余部分

    2024-07-21 12:38:02       16 阅读
  7. 【SQL】百万千万级最大表如何添加字段

    2024-07-21 12:38:02       19 阅读
  8. 谓词 & lambda & bind()

    2024-07-21 12:38:02       15 阅读