环形链表的判断方法与原理证明

(题目来源:力扣)

一.判读一个链表是否是环形链表

题目:

解答:

方法:快慢指针法

内容:分别定义快慢指针(fast和slow),快指针一次走两步,慢指针一次走一步。

原理:若不成环,则快指针(或快指针的next)一定会走到空,据此,写出循环条件,当循环结束时,返回NULL

cs 复制代码
while(fast&&fast->next)

此时,问题来了,成环怎么办?不能再通过循环条件来判断(因为此时该循环是死循环了)

目光再回到快慢指针上,若成环,则快指针一定会有追上慢指针的时候,当它们相遇时,循环结束

证明:

当fast,slow都进入环时:
假设:fast,slow之间相距N(单位:节点)
fast一次走两步,slow一次走一步,则每走一次,二者之间的距离就减1
如此循环往复,距离一定会变为0,即一定相遇

解题代码(并非完整代码)

cs 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
bool hasCycle(struct ListNode *head) 
{
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    while(fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
        if(fast == slow)
        {
            return true;
        }
    }
    return false; 
}

拓展:

fast走3步可以吗?n步呢?

当fast一次走3步时:
假设:fast,slow之间相距N(单位:节点)
fast一次走三步,slow一次走一步,则每走一次,二者之间的距离就减2,变为N-2

情况1:N为偶数,则距离一定会变为0,即相遇

情况2:N为奇数

距离 会逐渐递减为**-1**(N-2,N-4,...3,2,-1)(这里的-1是指fast领先slow指针一步)

fast和slow错过

假设环的总长度为C ,那么fast,slow间的距离就变为C-1,继续分类讨论

情况2.1:C-1为偶数,则一定会相遇

情况2.2:C-1为奇数,则二者之间的距离最终又会变为-1,一直错过无法相遇

总结:当N为奇数,C为偶数时,二者一直错过,无法相遇

真的是这样吗?

C是偶数且N是奇数,这个条件能同时满足吗?

不妨推导一下:

假设:slow 走过的路程为L,当slow进入环时,fast已经绕环走了x (x>=0) 圈,环的长度为C

(slow 走过的路程简记为slow,fast同理)

slow = L

fast = L + x*C + C-N

3*slow = fast

则 3*L = L + x*C + C-N

化简得 N =(x+1)* C - 2*L

C是偶数,2*L也是偶数,N一定是偶数,不可能是奇数,所以此条件不可能同时成立,fast与slow一定会相遇

(fast走n步的情况就交给读者自行思考了)

二.找到环形链表开始进入环的第一个节点

题目:

解答:

设进入环的第一个节点为 ptail

1.先判断是否是环形链表(同上)

2.找到开始进入环的第一个节点

方法:

先定义快慢指针:fast和slow,fast一次走两步,slow一次走一步

先让fast和slow相遇,定义meet指针指向相遇节点

定义一个新指针newhead 指向链表的头结点,然后让newhead和meet指针同时向前走,他们相遇的节点就是ptail

证明:

假设:slow 走过的路程为L + S(L是环外走过的路程,S是环内走过的路程),相遇时,fast已经绕环走了x圈(x>=1),环的长度为C

(slow 走过的路程简记为slow,fast同理)

(补充:slow和fast相遇时,S一定小于C,因为slow走完一圈之前,二者一定会相遇)

slow = L + S

fast = L + x*C + S

2*slow = fast

2*(L + S)= L + x*C + S

L = x*C - S = (x-1)* C + C - S

L 是newhead到ptail间的距离,(x-1)* C是环长度的整数倍,C - S是meet到ptai的距离

所以当newhead和meet一定相遇在ptail节点

证毕

解题代码(非完整代码)

cs 复制代码
struct ListNode *detectCycle(struct ListNode *head) 
{
    //先判断有无环,并找到相遇时的节点
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    struct ListNode* meet = NULL;
    while(fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
        //相遇
        if(fast == slow)
        {
            meet = fast;
            struct ListNode* newhead = head;
            while(newhead != meet)
            {
            newhead = newhead->next;
            meet = meet->next;
            }
            return meet;
        }
    }
    return NULL;
}
相关推荐
星星火柴9361 小时前
关于“双指针法“的总结
数据结构·c++·笔记·学习·算法
艾莉丝努力练剑2 小时前
【洛谷刷题】用C语言和C++做一些入门题,练习洛谷IDE模式:分支机构(一)
c语言·开发语言·数据结构·c++·学习·算法
闪电麦坤953 小时前
数据结构:迭代方法(Iteration)实现树的遍历
数据结构·二叉树·
C++、Java和Python的菜鸟3 小时前
第六章 统计初步
算法·机器学习·概率论
Cx330❀3 小时前
【数据结构初阶】--排序(五):计数排序,排序算法复杂度对比和稳定性分析
c语言·数据结构·经验分享·笔记·算法·排序算法
散1123 小时前
01数据结构-Prim算法
数据结构·算法·图论
起个昵称吧4 小时前
线程相关编程、线程间通信、互斥锁
linux·算法
阿巴~阿巴~4 小时前
深入解析C++ STL链表(List)模拟实现
开发语言·c++·链表·stl·list
..过云雨4 小时前
01.【数据结构-C语言】数据结构概念&算法效率(时间复杂度和空间复杂度)
c语言·数据结构·笔记·学习
myzzb4 小时前
基于uiautomation的自动化流程RPA开源开发演示
运维·python·学习·算法·自动化·rpa