0
点赞
收藏
分享

微信扫一扫

【神秘海域】[动图] 结合题目-手把手带你剖析 “带环链表“

image-20220501153114604


文章目录

引言

🌈
上一篇 【神秘海域】数据结构与算法 内容是 单链表及其接口

而本篇内容是对单链表的一个 非常重要 的补充: 带环单链表 。它,是大厂面试时可能会提问的内容,非常的重要!

本篇就是要结合题目来 详细分析一下 单链表的带环问题

带环单链表之前 : 快慢指针

🌈
在详细分析 带环单链表 之前,先分析两道题来了解一个非常重要的算法思路:快慢指针

题1:单链表的中间结点

🌈
原题描述是这样的:

示例 2

原题链接: Leetcode - 876. 链表的中间结点

这一题的解法,就需要使用到 快慢指针 的思路

那么什么是 快慢指针 ?即,使用两个 移动速度不同 的指针在 数组链表 等 序列结构上移动。

代码实现:

struct ListNode* middleNode(struct ListNode* head)
{
    struct ListNode* pfast = head;
    struct ListNode* pslow = head;
    while(pfast && pfast->next)
    {
        pfast = pfast->next->next;
        pslow = pslow->next;
    }

    return pslow;
}

image-20220430201513575

题2:链表中倒数最后k个结点

🌈
此题描述是这样的:

示例 2:

原题链接:Nowcoder - JZ22 链表中倒数最后k个结点

本题的思路也是使用快慢指针,但是与上一题不同的是,本题是先走为快指针 与 后走为慢指针

代码实现:

struct ListNode* FindKthToTail(struct ListNode* pHead, int k )
{
    struct ListNode* pfast = pHead;
    struct ListNode* pslow = pHead;
    
    while(k--)
    {
        if(pfast)
        {
            pfast = pfast->next;
        }
        else
        {// 快指针指向空,即链表长度不到 k,直接返回 NULL
            return NULL;
        }
    }
    while(pfast)
    {
        pfast = pfast->next;
        pslow = pslow->next;
    }

    return pslow;
}

image-20220430203858767


在分析带环链表之前,需要 需要了解一下 快慢指针 ,因为 带环链表的分析 是根据 快慢指针 分析的.

带环链表分析

🌈
分析 带环链表 ,先 由一道题来引入:

题:环形链表

🌈
此题描述:

示例 2

示例 3

原题链接:Leetcode - 141. 环形链表

本题的思路也非常简单:

代码实现:

bool hasCycle(struct ListNode *head)
{
    if(head == NULL)
        return false;

    struct ListNode* pfast = head;
    struct ListNode* pslow = head;

    while(pfast && pfast->next)
    {
        pfast = pfast->next->next;
        pslow = pslow->next;

        if(pfast == pslow)
            return true;
    }

    return false;
}

image-20220430210039173

OK,带环链表的题做出来了

但是并没有结束 如果只是这样 怎么会有大厂提问呢?


带环链表的问题

🌈
链表带环 的基础上,还会延伸出几个问题:

  1. 快指针一次走两步,慢指针一次走一步,两指针一定会相遇吗?为什么?
  2. 如果 快指针一次走两步呢?三步呢?四步呢?为什么?
  3. 怎么找到带环链表的 入环节点

这才是 带环链表 真正需要知道的东西~

⭐带环链表深入分析⭐ *

🌈

问题1

🌈
快指针一次走两步,慢指针一次走一步,两指针一定会相遇吗?为什么?

来详细分析一下:

画图抽象图来分析,一个带环链表,抽象的形式可以看作:image-20220430232045310

快慢指针 同时 从首节点开始走,快指针走得快,慢指针走得慢

所以慢指针入环时,快指针早就已经入环了

此时的情况可能是(设一下,只是假设)image-20220430232210782

两个指针都入环之后,快指针开始在环内追逐慢指针:

因为 当这样的两个指针都入环之后,两个指针之间的距离变化就变为了 每走一步减一

所以,必定会相遇

问题2

🌈
如果 快指针一次走三步呢?四步呢?为什么?

快指针一次走多步,就需要看情况来分析了

快指针一次走三步:

上边我们分析了 快指针一次走两步 时的相遇情况:当两个指针都入环之后,其之间的距离是以 每次缩小 1 变化的

那么如果 快指针一次走三步,那么 两个指针都入环之后,其之间的距离就是 以 每次缩小 2 变化的

每次缩小 2,会造成什么情况呢?

快指针一次走四步:

当快指针 一次走四步 的时候,按照 一次走三步 的思路进行分析

  1. X3 的倍数,可以相遇
  2. X 不为 3 的倍数,且 C-1C-2 也不为 3 的倍数,就永远无法相遇
  3. C-1C-2 ,需要更详细的分析

也就是说,快指针 一次走多步 能不能与慢指针相遇是 不确定的

实际的情况,与 环的长度入环前链表的长度 都有关系,需要 具体情况具体分析

问题3

🌈
怎么找到带环链表的 入环节点

能够找到入环节点的一个前提是:快指针已经与慢指针相遇

详细分析一下:

首先还是画图假设一下:image-20220501142311619

参考图来看,慢指针 从开始与快指针相遇,走过的距离就是 :L + X

那么 快指针 走过的距离就是 : 2 * (L + X)

快指针走过的距离还可以怎么表示呢?

快指针走过的距离 还可以这样表示:L + X + N * C (N表示走过的圈数)

所以,快指针 从开始与慢指针相遇 走过的距离,就可以写成一个等式:

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

化简一下就是: L + X = N * C

这个式子有什么用呢?

image-20220501142311619

其实,这个等式说明:

如果,有两个指针同时以一次一步的速度,一个从 链表的首节点 开始,另一个从 快慢指针相遇点 开始,两个指针会在环的入口节点相遇。

为什么呢?

L + X = N * C 可以写为 --> L = N * C - X

一个指针从 链表首届点开始走,走过 L 长度 它的位置在入环节点

一个指针从 快慢指针相遇点 开始走, 走过 N * C 的长度,它的位置还在 快慢指针相遇点 ,但是如果走过 N * C - X 的长度,那么它的位置就也在 入环节点了,因为 入环节点到快慢指针相遇点的距离是 X

此时,入环节点就找到了。

题:寻找入环节点

🌈
分析完如何寻找入环节点,下面来尝试把这道题给做了:

题目描述:

示例 2

示例 3

原题链接:Leetcode - 142. 环形链表 II

代码实现:

// 大体思路与判断有环差不多
// 但是 有环时不能直接返回
struct ListNode *detectCycle(struct ListNode *head)
{
    if(head == NULL)
        return NULL;

    struct ListNode* pfast = head;
    struct ListNode* pslow = head;

    while(pfast && pfast->next)
    {
        pfast = pfast->next->next;
        pslow = pslow->next;

        if(pfast == pslow)		// 有环
        {
            struct ListNode* phead = head;
            while(phead != pslow)		//使 两个指针 分别从 首节点和相遇点 一次一步 移动,直到相遇
            {
                phead = phead->next;
                pslow = pslow->next;
            }
            return phead;
        }
    }

    return NULL;
}

image-20220501150938765


结语

🌈
本篇是对 单链表带环问题 的一个深入探索,单链表带环问题是 单链表中一个非常重要的应用 和 对单链表非常重要的理解。同时,他已经进入了大厂面试可能会考的范畴,重要的是对 单链表带环问题的深入分析而不是简单的判断是否有环

本篇文章到此结束

感谢阅读~


求评论、点赞、收藏~


举报

相关推荐

0 条评论