算法与数据结构面试分享(十六)判断一个单链表是否含环,返回入口点

题目:现在有一个单链表,请判断它是否带环,如果构成环,请返回环的入口点。

这是接着上篇文章的讨论,留了两个问题,如何判断一个单链表是否构成环,有的话,返回它的入口点。之后我们再研究在构成环的链表中如何判断两个链表是否相交,这样我们上篇文章就完整了。

上篇当中,我们提到了构成环的结构应该是类似于6的样子对吧。我们回忆一下,小学做过的数学题哈,两个小朋友在操场上跑步,一个快,一个慢,请问他们是否会相遇。大家都知道快的会追上慢的对不对?现在的问题稍微要复杂一点,因为这个操场上多了一个直线型的跑道,我们要找出来,直线型跑道与圆形跑道的交点。我们先看图,然后我给出一些定义,带大家做些简单的数学推导:

  • L: 单链表的长度
  • A-B: 头结点到入口点的长度为a,B节点是我们要求的目标节点
  • B-C: 入口点到相遇点的长度为x
  • B-C-B: 环的长度为r
  • S: 出发到相遇是的长度


那我们能够有这样的推导:

  • 每次走一步的指针,S = a+x
  • 每次走两步的指针一共走了 2S, 
  • 我们能够肯定它从A-B-C, 但是环内走了多少圈,不得而知,假设在环内已经绕了N圈,之后等到了慢的同学,则有 2S = S + Nr , 所以 s = Nr, 
  • 我们就得到了 a + x = Nr,  a + x  = (N-1) r + r
  •  我们知道 L = a + x + (r-x), r-x 也就是 C-B,而  L = a + r, 我们得到 a + x  = (N-1)r + (L-a), 
  • 所以 a = (N-1)r + (L-a-x)

实际意义: 上面的这个式子很关键,我们的目标是求出a,这意味着,我们如果一步一步的走,从A出发到入口点需要走的步骤,正好等于从相遇点到B点的步数。(L-a-x)其实就是图中紫色的大板块饼。也就是说,在相遇的位置,我们重新设一个指针,然后两个指针一起走,相遇的点就是入口点。

问题变得简单了,我们来看代码,至于怎么判断有环的,大家可以从里面体会出来,也可以关注我后序的视频课程博客公众号推送。

/// <summary>
        /// 判断单链表是否有环,如果有的话我们返回入口点,否则为null
        /// </summary>
        /// <param name="linkNode">单链表</param>
        /// <returns></returns>
        public static MyLinkNode<T> FindEntryInCycle(MyLinkNode<T> linkNode)
        {
            var fast = linkNode;
            var slow = linkNode;

            MyLinkNode<T> meetPoint = null;

            do
            {
                //  如果Slow 的 next有值,每次让 Slow 走一步
                if (slow.Next != null)
                {
                    slow = slow.Next;
                }
                // 如果 fast 的 next 有值,而且 fast 的 next 的 next还有值,让fast走两步
                if (fast.Next != null && fast.Next.Next != null)
                {
                    fast = fast.Next.Next;
                }

                // 走完一次,我们判断下,相遇了没有
                if (fast == slow) meetPoint = fast;
            }
            // 如果没有相遇, 也没有到链表末尾,那我们继续,否则退出 do-while
            // 大家想想为什么用 do-while, 而不是 while 哈
            while (fast != slow && fast != null && slow != null);

            // 不为空,说明有环,我们继续找相遇点, 否则不是环
            if (meetPoint != null)
            {
                var newPoint = linkNode;

                while (newPoint != slow)
                {
                    newPoint = newPoint.Next;
                    slow = slow.Next;
                }

                return newPoint;
            }

            return null;
        }

在这个基础上,再考虑两个可能有环的链表相交问题就变得简单了。下篇再续。好了,欢迎大家关注我的公众号,还有我的系列视频教程, 数据结构与算法 微软经典算法面试题辅导。大家有什么更好的解法,也欢迎讨论哈。

 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值