快慢指针:如何判断单链表是否有环

        本题是苦主找实习的面试真题,想了半天,想出一个暴力解法:

                1.首先定义一个哈希集合hS,其val为ListNode。【面试的时候把哈希集合忘了,用的是数组……】

                2.遍历链表,判断

                        1st.哈希集合中,是否存在该结点。存在就说明有环,没什么好说的。

                        2nd.如果不存在,把结点添加进hS。

        苦主痛定思痛,一下面试就立马百度答案,发现用的竟然是快慢指针。

        苦主愣住了,竟然还是经典的链表三板斧:栈、快慢指针、哑节点之一啊!

题目定义:

        1.链表:本题链表为单链表,链表头节点存在数据,保证无特殊情况。

        2.父辈节点的定义:对于链表a->b->c->d,如果认为a是b、c、d的父辈(或祖宗辈)节点。

        3.环:存在一个节点X,它指向其父辈(或祖宗辈)结点,说明有环。【本质就是,有一个节点存在两个前驱节点(当然,如果是头节点,仍是一个,所以这个规律用于判断环时,一致性较差,需要额外讨论)

        4.环的性质:我们可以发现,一旦链表中存在环,那么链表不会出现一个节点D,其下一个节点为null,也就是D.next!=null。

快慢指针的定义和额外问题:

        1.快慢指针:定义两个指针fast和slow,fast每次移动的“距离”长于slow,这就是快慢指针。

        问题1:如何判断是否有环?

                判断有无,使两个指针从头节点出发,令fast每次走两步,slow每次走一步。最终,如果fast指向null,说明无环;;如果fast与slow相遇,说明一定有环【朴素的理解是:既然fast走得那么快,在单向的道路上,slow怎么能赶上fast呢?只有类似操场,slow才能赶上fast,所以才会相遇】

                证明:fast与slow的关系可以证明有无环。

                        1.无环链表:最终fast一定指向null,两者互为充分必要条件,不在此说明。

                        2.有环链表:其难点,主要是“为什么一定相遇?有没有可能会错开?”

                        证明:

                                如果我们把环链表看成一个长度为n的数组,如果遍历结束,指针指向下标为0的数据,如图:

                                对于有环链表,我们可以看到,最终fast一定会落在slow的“后面”,此时fast和slow可能差距N个格子。

                                1.差距N个格子:比如fast指向1,slow指向9,此时差距8格,fast进一步,指向3,slow进一步,指向10,此时差距7格,我们甚至可以把数组延长,即12后加上0、1、2……【也可能环从4、5开始】,可以发现,二者越来越近。总之,由于fast每次走得比slow多一格,因此最终一定会差距在2个格子。

                                2.差距2个格子:比如fast指向1,slow指向3,此时差距两格,fast进一步,刚好指向3,slow进一步,指向4。此时差距一个格子。

                                3.差距1个格子:比如fast指向3,slow指向4,fast进一步,指向5,slow进一步,指向5,二者相等,此时证明有环。

        问题2:如何得到入环点?

                入环点:从链表的角度说,即有两个前驱节点的节点【也可能是有一个前驱节点的头节点】

                知道有环,无法解决环路问题,需要拿到最末尾的节点,使其指向null,才能解决。

                借助一个新指针ptr,从头节点开始,每次进一位,同时使slow每次进一位,最终slow与ptr重合的地方,即入环点。

                证明:为什么slow继续走,和ptr重合时,就是入环点?

                        假设该链表为0->1->2->3->4->5->6->2->3->4……即下图:

                        目前看不出什么,我们首先要确定的,是fast要经过多少步,才能和slow相遇?

                        如果把环看成一个操场,fast走的速度是slow的两倍,同样时间,fast走的距离也是slow的两倍,因此,fast至少走完一个环,slow最多走完一个环。

                        证明:如果链表为0->1->2->3->4->0->1->2……即指向头节点的环,fast、slow同步出发,那么有以下步骤:

                                1.fast指向2,slow指向1;;

                                2.fast指向4,slow指向2;;

                                3.fast指向1,slow指向3;;(fast走完第一个环)

                                4.fast指向3,slow指向4;;

                                5.fast指向0,slow指向0;;(fast走完第二个环,slow走完第一个环)

                        如图,对于入环点是头节点head的链表,如果把a到b的距离S,看成环的长度,那么slow相当于从a'出发(也就是第二圈)

                        我们已经证明了,在有环时,fast与slow的距离不断缩近。并且每次接近一格,不会发生slow就在面前,却直接略过的情况。

                        所以,对于入环点是其它节点【非头节点】的链表,如果把头节点到入环点的距离设为HI,那么fast相当于提前了HI的距离,slow绝对走不完第一圈,就会被赶上。

                        如图:(该图理解为H……后面接着a,勉强看吧)

        此时,ptr与入环点a距离HI,如果slow入环后,走了dealt步与fast相遇,那么slow走了dealt+HI步,如果一个环的长度是X,那么此时slow距离入环点长度为X-dealt;

        如此,只需证明HI与X-dealt大小成倍数即可。【HI=kX-dealt,k>=1】

        设fast走了n圈,那么fast一共走了HI+nX+dealt;

        又因为fast走的总距离,是slow的两倍(因为fast进二,slow进一)

        所以,2*(HI+dealt)=HI+nX+dealt;

        消除,得到HI=nX-dealt;

        如此,如果slow继续走HI步,就会走nX圈,外加dealt步,刚好到达入环点。

        并且,ptr走了HI步,刚好到达入环点。

        证明结束。

以上内容即我想分享的关于快慢指针的一些知识。

        我是蚊子码农,如有补充,欢迎在评论区留言。个人也是初学者,知识体系可能没有那么完善,希望各位多多指正,谢谢大家。

  • 17
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本系统的研发具有重大的意义,在安全性方面,用户使用浏览器访问网站时,采用注册和密码等相关的保护措施,提高系统的可靠性,维护用户的个人信息和财产的安全。在方便性方面,促进了校园失物招领网站的信息化建设,极大的方便了相关的工作人员对校园失物招领网站信息进行管理。 本系统主要通过使用Java语言编码设计系统功能,MySQL数据库管理数据,AJAX技术设计简洁的、友好的网址页面,然后在IDEA开发平台中,编写相关的Java代码文件,接着通过连接语言完成与数据库的搭建工作,再通过平台提供的Tomcat插件完成信息的交互,最后在浏览器中打开系统网址便可使用本系统。本系统的使用角色可以被分为用户和管理员,用户具有注册、查看信息、留言信息等功能,管理员具有修改用户信息,发布寻物启事等功能。 管理员可以选择任一浏览器打开网址,输入信息无误后,以管理员的身份行使相关的管理权限。管理员可以通过选择失物招领管理,管理相关的失物招领信息记录,比如进行查看失物招领信息标题,修改失物招领信息来源等操作。管理员可以通过选择公告管理,管理相关的公告信息记录,比如进行查看公告详情,删除错误的公告信息,发布公告等操作。管理员可以通过选择公告类型管理,管理相关的公告类型信息,比如查看所有公告类型,删除无用公告类型,修改公告类型,添加公告类型等操作。寻物启事管理页面,此页面提供给管理员的功能有:新增寻物启事,修改寻物启事,删除寻物启事。物品类型管理页面,此页面提供给管理员的功能有:新增物品类型,修改物品类型,删除物品类型。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值