< 数据结构 > 单向环形链表

简介: 目录1、例题引入2、何为带环链表3、题解思路4、拓展问题 (1)slow一次走1步,fast一次走2步,一定能追上吗? (2)slow一次走1步,fast一次走3步,能追上吗?fast一次走4步呢?n步呢? (3)链表环的入口点在哪呢?

1、例题引入

  • 链接直达:

环形链表

  • 题目:image.png

2、何为带环链表

正常的单链表每个节点顺次链接,最后一个节点指向NULL,如下:image.png 而带环链表的最后一个节点不再指向NULL了,指向的是前面任意一个节点,以此形成带环链表,并一直循环下去。如下:image.png3、题解思路

我们可以将上述图画的抽象一点,在没有进入环之前我们用直线表示,进入环之后用圈来表示,以此示意循环。此题需要用到我们之前讲解的求中间节点和求倒数第k个节点的快慢指针的思想。定义两个指针slow和fast均指向一开始的位置。 让slow一次走一步,fast一次走两步。

image.png 当slow走到直线一半的位置时,此时的fast刚好就在环的入口点。image.png假设slow刚好走到环的入口点时,fast走到如下位置,此时fast开始追赶模式

image.png

fast开始追赶slow,假设fast在如下的位置开始追上slow

image.png

  • 代码如下:
bool hasCycle(struct ListNode *head) {
    struct ListNode*slow=head;
    struct ListNode*fast=head;
    while(fast&&fast->next)
    {
        slow=slow->next;
        fast=fast->next->next;
        if(slow==fast)
        {
            return true;
        }
    }
    return false;
}

单纯从解体的角度看,此题并不复杂,仅需用到快慢指针的思想即可解决,单是由此题可以引出多个值得我们探讨的问题,以此来加深我们对环形链表的认知,如下三大拓展问题:


4、拓展问题

(1)slow一次走1步,fast一次走2步,一定能追上吗?

答案:一定能。

证明:

当slow走到中间的时候,fast一定进环了,此时fast开始追击。我们假设slow进环以后,slow和fast的距离是N,此时slow走1步,fast走2步,它们俩的距离缩短1变为N-1。以此类推,每次追击,距离缩小1,当距离缩小为0时就追上了。综上,一定能追上。image.png(2)slow一次走1步,fast一次走3步,能追上吗?fast一次走4步呢?n步呢?

答案:不一定

证明:

我们先来讨论slow一次走1步,fast一次走3步的情况。假设slow走了1步,fast走3步时刚好进环,而当slow刚好进环的时候,fast可能已经走了1圈,具体情况得看环的大小,此时slow和fast之间的距离为N。并假设环的长度是C。


slow一次走1步,fast一次走3步,距离变为N-2。由此可见,fast和slow每走一次,距离缩短2。此时就不难发现了,需要分类讨论,当N是偶数时,刚好可以追上,当N是奇数时,追到最后距离为-1,此时就要再追了,意味着slow和fast之间的距离变成C-1。


继续追击,根据前面的分析,如果C-1是偶数,那么可以追上。如果C-1是奇数,那么就永远追不上了,将会无线循环追下去,可就是追不上。他们的差距N是由进环前的长度和环的长度决定的,而这两个又都是随机的,所以N的值不确定,可奇可偶,又像刚刚那样讨论下去,出现奇数将一去不复返。image.png同理fast一次走4步也是这样的讨论,同样都是不一定,不过这个时候是每走一次,距离缩短3。当N是3的倍数就可以追上,当不是3的倍数就要继续讨论了,有兴趣的童鞋可以继续钻研下去,思想和fast一次走3步一样,这里不过多赘述。


(3)链表环的入口点在哪呢?

当我们搞清楚slow和fast分别走的距离时,入口点自然就明了了。


法一:

image.pngslow一次走1步,fast一次走2步,那么fast走的距离是slow的2倍


在具体讲解之前,首先要搞清楚,不存在说慢指针slow在里头走了一圈,快指针fast还没有追到slow,因为fast每次走2步,slow每次走1步,它俩间的距离每次都缩小1,所以只会越来越近,直到追到。最多最多也就快1圈,但从来也不会刚好满1圈。所以下面很容易推出slow和fast分别走了多少。


假设:

【链表头 - - - 入口点】:L

【入口点 - - - 相遇点】:X

【环的长度】:Cimage.pngslow走的距离:L + X


fast走的距离:L + N*C + X


解释:

因为先前已经提到slow不会都走了一圈还没被追到,所以很容易推出slow的距离就是L+X


而快指针一次走2步,很可能会因为环过小导致在slow指针进入入口点前,fast指针已经走了好几圈。简而言之3句话:


L很小,C很大,slow进环前,fast可能在环里面,一圈都没走完

L很大,C很小,slow进环前,fast在里面走了很多圈了

但是slow进环以后,在一圈之内,fast一定追到slow,它们的距离最多C-1

根据一开始说的,fast走的距离是slow走的距离的2倍,可列出如下式子:


2*(L+X) = L + N*C + X


化简后:L+X = N*C     或    L = N*C - X     或     L = (N-1)*C + (C-X)     或     L + X = N*C


用此公式即可证明:一个指针从meet走,一个指针从head走,他们会在入口点相遇!


因为式子(N-1)*C表明从相遇点走了N-1圈后又回到了相遇点,此时再走C-X的距离就回到了入口点,由上得知,此公式确实让它们回到了入口点。


用一道切实的题目来具体解出入口点的位置:

链接直达:

环形链表2.0-->寻找入口点


题目:image.png

  • 代码如下:
struct ListNode* detectCycle(struct ListNode* head) {
    struct ListNode* slow = head;
    struct ListNode* fast = head;
    while (fast && fast->next)
    {
        //判断是否是带环链表
        slow = slow->next;
        fast = fast->next->next;
        if (slow == fast)
        {
            struct ListNode* meet = slow;
            while (meet != head)
            {
                //求出相遇点
                meet = meet->next;
                head = head->next;
            }
            return meet;
        }
    }
    return NULL;
}
  • 求相遇点还有另外一种方法:

找到相遇点meet后,让meet做尾,让下一个点做新链表的头image.png此法显的尤为巧妙,刚好转换成了两个链表求交点的问题。因为此时headA链表的尾部是meet,而headB链表的尾部也是meet,此时就意味着俩链表必会相交,而相交的地方就是入口点,两链表相交正是博主上篇博文中所详细讲解的,这里就不过多强调了。

相关文章
|
2月前
|
存储 算法 Perl
数据结构实验之链表
本实验旨在掌握线性表中元素的前驱、后续概念及链表的建立、插入、删除等算法,并分析时间复杂度,理解链表特点。实验内容包括循环链表应用(约瑟夫回环问题)、删除单链表中重复节点及双向循环链表的设计与实现。通过编程实践,加深对链表数据结构的理解和应用能力。
65 4
|
14天前
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
2月前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
75 5
|
2月前
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
122 4
|
2月前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之单双链表精题详解(9)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
2月前
|
存储 Web App开发 算法
2024重生之回溯数据结构与算法系列学习之单双链表【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构之单双链表按位、值查找;[前后]插入;删除指定节点;求表长、静态链表等代码及具体思路详解步骤;举例说明、注意点及常见报错问题所对应的解决方法
|
2月前
|
算法
数据结构之购物车系统(链表和栈)
本文介绍了基于链表和栈的购物车系统的设计与实现。该系统通过命令行界面提供商品管理、购物车查看、结算等功能,支持用户便捷地管理购物清单。核心代码定义了商品、购物车商品节点和购物车的数据结构,并实现了添加、删除商品、查看购物车内容及结算等操作。算法分析显示,系统在处理小规模购物车时表现良好,但在大规模购物车操作下可能存在性能瓶颈。
57 0
|
2月前
|
C语言
【数据结构】双向带头循环链表(c语言)(附源码)
本文介绍了双向带头循环链表的概念和实现。双向带头循环链表具有三个关键点:双向、带头和循环。与单链表相比,它的头插、尾插、头删、尾删等操作的时间复杂度均为O(1),提高了运行效率。文章详细讲解了链表的结构定义、方法声明和实现,包括创建新节点、初始化、打印、判断是否为空、插入和删除节点等操作。最后提供了完整的代码示例。
84 0
|
3月前
|
存储
[数据结构] -- 双向循环链表
[数据结构] -- 双向循环链表
30 0
|
3月前
|
存储
探索数据结构:便捷的双向链表
探索数据结构:便捷的双向链表