算法热门:环形链表II(LeetCode 142)

Posted 白龙码~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法热门:环形链表II(LeetCode 142)相关的知识,希望对你有一定的参考价值。

Part I、回顾环形链表I

环形链表I的详细内容可以参考我们之前一期的文章
《算法热门:环形链表I(LeetCode 141》)
这一期,我们将在上一期的基础上再拓展新的内容,不过在此之前,先简单回顾一下环形链表I的主要思想:

1、快慢指针思想

我们定义快、慢两个指针,假设慢指针一次走一个结点,快指针一次走两个结点,那么快指针必然会在慢指针之前走到链表环的入口点——

当链表的头到环的入口之间的距离足够大而环的长度又比较小时,由于快指针先入环,那么在慢指针好不容易走到换入口时,快指针可能已经在环内绕了N圈了,其中N≥0,N∈N*

当这种循环再进行下去,fast指针如果追上slow指针,就能判断出,链表有环;
问题来了,fast指针真的会追上slow指针吗?

2、追及思想


我们假设链表的头到环的入口的距离为L,环的长度为C,slow进入环后,与fast之间的距离为X;
(由于slow和fast都是顺时针走的,这里取顺时针间距,如上图所示)

由于fast指针一次走两个节点,slow一次走一个节点,也就是说,fast指针比slow指针多走一步,那么如果它们继续走下去,则fast指针必然会追上slow;

如果还不理解,我们不妨把这样的一个追逐问题化为一条直线上的追逐问题:

假设还需走n次fast追上slow,那么也就是说:

解出来:n=x

也就是说,只要这样的循环再进行x次,fast必然会追上slow;

另一方面考虑,如果链表没有环,那么由于fast走得快,slow永远不可能追上它,从而我们可以判定:环存在!

3、关于n = x的补充

值得一提的是,由于x是慢指针入环的时候,快慢指针之间的距离,那么这里x必然会小于等于C-1,其中C是环的长度。
原因也很简单:因为此时快指针和慢指针都已经在环里了,那么最好的情况是这样:

最坏的情况就是:

由于fast指针走n次就可以追上慢指针,且n=x,那么由此我们可以断定:在慢指针刚入环还没走完一圈时,就会被快指针追上!

Part II、环形链表进阶

1、题目简介

当我们有能力判断一个链表是否带环的时候,我们能否更进一步,把环形链表的环入口给找到呢?
请看题:

2、题目分析

简单来说,这题的要求就是:如果链表带环,则返回环的入口指针,如果不带环,则返回NULL;
如何实现呢?这跟之前的那题有啥子关联?有,关联还很大。
我们同样采取快慢指针的方法来判断这个链表是否带环,如果带环,则快慢指针必然相遇在环内的某个地方,如图:

假设从链表的头指针到环的入口,这一段的距离为L,快慢指针的相遇点到环入口,这一段的距离为X,环的长度为C。

由于慢指针在环内还没走完一圈就会被快指针追上,所以当快慢指针相遇时,慢指针走的距离为L+X。
(详细原因见Part I 中第三点:关于n=x的补充)

当L足够大而C又比较小时,由于快指针先入环,那么在慢指针好不容易走到换入口时,快指针可能已经在环内绕了N圈了,因此当快慢指针相遇时,快指针走的距离为L+X+NC(N≥0,N∈N

由于快指针的步长是慢指针的两倍,所以快指针走的距离也是慢指针走的距离的两倍即:

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

由此得到

L= N * C - X = (N - 1) * C + C - X

那么,如果此时一个指针p1从链表的头出发,另一个指针p2从快慢指针的相遇点出发,两指针的步长都为1,则经过(N - 1) * C + C - X次的移动,p1从链表的头走过L这段距离来到环的入口,p2走了 (N - 1) * C步回到相遇点,又走了C - X步来到了环的入口。此时,p1=p2=环入口的指针。

理论分析结束,代码实现:

struct ListNode *detectCycle(struct ListNode *head)
{
    struct ListNode* slow = head, *fast = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast)//slow和fast相遇,链表带环
        {
            struct ListNode* p1 = head, *p2 = slow;//p1从链表的头出发,p2从相遇点出发
            while(p1 != p2)
            {
                p1 = p1->next;
                p2 = p2->next;
            }
            return p1;//p1和p2相等时,返回任意一个
        }
    }
    return NULL;
}

Part III、总结

这是一道非常值得收藏回味的题,和环形链表I类似,环形链表II的代码实现同样很简单,但是关于链表带环的题目最重要的是实现背后的逻辑推理和分析。
本题还有另外一个解题思路,需要一定的铺垫,放在之后介绍~

创作不易,留个点赞评论再走吧~关注我,定期分享更多学习、刷题经验

以上是关于算法热门:环形链表II(LeetCode 142)的主要内容,如果未能解决你的问题,请参考以下文章

算法-leetcode-142. 环形链表 II

Java算法 每日一题 编号142:环形链表 II

Java算法 每日一题 编号142:环形链表 II

Java算法 每日一题 编号142:环形链表 II

LeetCode Java刷题笔记—142. 环形链表 II

[LeetCode]142. 环形链表 II