链表中环的检测

综述

关于链表中环的检测,相关问题一般有如下几种:

  1. 给定一个单链表,判断其中是否有环的存在
  2. 如果存在环,找出环的入口点
  3. 如果存在环,找出环上结点的个数
  4. 如果存在环,求出链表的长度
  5. 如果存在环,求出环上距离任意一个结点最远的结点(环的对面结点问题)
  6. 判断两个无环链表是否相交
  7. 如果相交,求出第一个相交结点

针对如上七种问题,下面我们逐一进行分析并写出相应实现代码。

判断是否有环

问题分析

对于这个问题,有一种非常巧妙的“快慢指针”的方法,就是定义两个指针:fast和slow,最初的时候fast和slow都指向链表的初始结点head,然后每一次操作,fast向前走两步,slow向前走一步。
因为fast比slow移动快,如果有环,那么fast一定会先进入环,而slow后进入环。当两个指针都进入环后,经过一定次数的操作,fast和slow最终会在环上相遇,并且一定是在slow绕环走完一圈之前相遇。


图形示例

如图所示,slow进入环时,fast可能处于图示状态,然后每次操作,slow会向前走一步,而fast会向前追两步。因此每次操作完fast到slow的距离都会缩短一步,5、4、3、2、1...直到相遇。
又因为同一个环中,slow和fast的距离不可能大于环的总长度,所以fast和slow一定会在slow走完一圈之前相遇。
特殊情况:开始时,slow和fast就在环的入口处,这样相遇时,slow刚好走完一圈。

实现代码

typedef struct node{
    char data;
    node *next;
}Node;
bool exitLoop(Node * head){
    Node *fast , *slow;
    slow = fast = head;

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

找出环的入口点

这个题也比较巧妙,需要先推导一下,最好准备一下纸笔,跟我一块算一下。
[图片上传中...(image-dc6092-1584686314842-3)]

问题分析

如果链表有环,则在slow绕环走完一圈之前,一定会和fast相遇。
我们假设相遇时slow走了s个结点,则fast走了2s个结点。
设环的长度为r,相遇时fast已绕环走了 n 圈(n>=1),可得等式:
2s = s + n *r 简化得 => s = n *r
又设head与环的入口点距离为 a ,入口点与相遇点的距离为 x ,根据slow的总路径为s可得:
s = a + x
结合上式:a + x = n *r 变形 => a + x = ( n - 1 ) *r + r
设链表总长度为L,则:r = L - a ,带入上式,得:a = ( n - 1 ) * r + ( L - a - x )
好了,注意看图,L - a - x 的长度就是从相遇点到环入口点的长度,也就是说,如果我们放置两个指针p1和p2,分别从起始点和相遇点出发,那么在p2绕环 ( n - 1 ) 圈后,最终一定会在环的入口点和p1相遇。
这样,我们就得到了环的入口点。

代码实现

typedef struct node{
    int elem;
    struct node * next;
}Node, *NodeList;

//寻找环的入口点
NodeList FindLoopPort(NodeList head)
{
    NodeList slow=head,fast=head;
    //得到相遇点
        while( fast->next != NULL && fast != NULL)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow==fast) break;
    }
    if(fast == NULL || fast->next==NULL)
        return NULL;
    //slow指在开头,fast指在相遇点
    //得到入口点
    slow=head;
    while( slow != fast ){
        slow = slow->next;
        fast = fast->next->next;
    }
    return slow;
}

找出环上结点的个数

问题分析

对于这个问题,有两种常见的解决方法:

  1. 在slow和fast相遇后,让其中一个继续向前走,下次相遇时,所经过的结点,就是环上的结点个数。
  2. 在slow和fast相遇后,让slow和fast同时继续向前走,下次相遇时,所经过的结点,就是环上的结点个数。

稍微解释一下第二种方法,因为slow和fast的速度不同,同时出发后,两指针最大距离为环的总长度r,然后每次操作距离都会缩短一位,最终一定会再次相遇。再次相遇时,正好操作r次,也正好经过r个结点。

方法一实现代码

typedef struct node{
    int elem;
    struct node * next;
}Node, *NodeList;

//寻找环的入口点
NodeList FindLoopPort(NodeList head)
{
    NodeList slow=head,fast=head;
    //得到相遇点
        while( fast->next != NULL && fast != NULL)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow==fast) break;
    }
    if(fast == NULL || fast->next==NULL)
        return NULL;
    //fast停止,slow继续向前走
    slow = slow->next;
    //得到再次相遇点
        int count=1;//计数器
    while( slow != fast ){
            count ++;
      slow = slow->next;
    }
    return count;
}

求出链表的长度

问题分析

链表长度L = 起点到入口点的距离 + 环的长度r ;
在前面的基础上,我们可以很轻松的得到这个题目的解。

求出环上距离任意一个结点最远的结点(环的对面结点问题)

环的对面结点示意图

问题分析

如图所示,结点1和结点4,结点2和结点5,结点3和结点6分别互为“对面结点”,也就是环上距离最远的结点。
我们依然可以使用“快慢指针”的思想来解决这道题,定义一个每次移动两个结点的指针fast和每次移动一个结点的指针slow,两指针同时在问题结点出发,当fast或者fast->next再次回到问题结点时,slow所指向的结点就是问题结点的“对面结点”。

代码实现

typedef struct node{
    int elem;
    struct node * next;
}Node, *NodeList;

//寻找对面结点
NodeList FindFacePort(NodeList prt0)
{
    NodeList slow=prt0,fast=prt0;
        do{
            slow = slow->next;
            fast = fast->next->next;
        } while(fast!=prt0 && fast!=prt0->next);
        return slow;
}

判断两个无环链表是否相交

问题分析

对于判断两个无环链表是否相交类的问题,看起来无从下手,但其实只需要转换一下思路就豁然开朗了。


相交链表示意图

如图所示,存在ListA和ListB两个无环相交链表,我们只需要将ListA的首尾相连,这样就变成我们熟悉的判断是否有环的问题了。
so easy ~


首尾相连后

然后是问题七:如果相交,求出第一个相交结点。
同理,可转化为求环的相交点的问题。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,012评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,589评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,819评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,652评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,954评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,381评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,687评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,404评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,082评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,355评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,880评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,249评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,864评论 3 232
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,007评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,760评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,394评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,281评论 2 259

推荐阅读更多精彩内容

  • 1. 怎么检测一个单向链表中是否有环 同样借助于快慢指针,思路如下: 定义 fast、slow 两个指针同时指向链...
    云飞扬1阅读 328评论 0 0
  • 链表中环的检测,就是判断链表中是否存在环形结构。带有环形结构的链表如下图所示: image.png 这里提供两种解...
    初心myp阅读 1,370评论 0 1
  • 题目描述: 这里考察的也是快慢指针。 当然,如果我们用 HashSet ,也可以实现。当时明显不是这道题的考察目的...
    sml_2阅读 1,541评论 0 0
  • 题目:给两个单链表,如何判断两个单链表是否相交?若相交,则找出第一个相交的节点。这道题的思路和解法有很多,在这把这...
    天堂鸟6阅读 595评论 0 3
  • 单链表 单链表问题与思路 找出单链表的倒数第K个元素(仅允许遍历一遍链表)使用指针追赶的方法。定义两个指针fast...
    wxkkkkk阅读 581评论 0 0