给定一个有环链表,实现一个算法返回环路的开头结点
来源:互联网 发布:买网络电视要注意什么 编辑:程序博客网 时间:2024/06/06 01:49
检测链表是否存在环路
有一种简单的做法叫FastRunner/SlowRunner法。FastRunner一次移动两步,而SlowRunner一次移动一步。这就好比两辆赛车绕着同一条赛道以不同的速度前进,最终必然会碰到一起。
聪明的读者可能会问:FastRunner会不会刚好“越过”SlowRunner,而不发生碰撞呢?绝无可能。假设FastRunner真的越过了SlowRunner,且SlowRunner处于位置i,FastRunner位于位置i+1。那么,在前一步,SlowRunner就处于位置i-1,FastRunner处于位置((i+1)-2)或i-1。也就是说,两者碰在一起了。
什么时候碰在一起?
假定这个链表有一部分不存在环路,长度为k。
若运用上述算法,FastRunner和SlowRunner什么时候会碰在一起呢?
我们知道,SlowRunner每走p步,FastRunner就会走2p步。因此,当SlowRunner走了k步进入环路部分时,FastRunner已走了总共2k步,进入环路部分已有2k-k步或k步。由于k可能比环路长度大得多,实际上我们应该把它写作mod(k, LOOP_SIZE)步,并用K表示。
对于之后的每一步,FastRunner和SlowRunner之间不是走远一步就是更近一步,具体要看观察的角度。也就是说,因为两者处于圆圈中,当A以远离B的方向走出q步时,同时也是向B更近了q步。综上,我们得出以下几点:
(1)SlowRunner处于环路中的0步位置;
(2)FastRunner处于环路中的K步位置;
(3)SlowRunner落后于FastRunner,相距K步;
(4)FastRunner落后于SlowRunner,相距LOOP_SIZE-K步;
(5)每过一个单位时间,FastRunner就会更接近于SlowRunner一步。
那么,两个节点什么时候相遇?若FastRunner落后于SlowRunner,相距LOOP_SIZE-K步,并且每经过一个单位时间,FastRunner就走近SlowRunner一步,那么,两者将在LOOP_SIZE-K步之后相遇。此时,两者与环路起始处相距K步,我们将这个位置称为CollisionSpot。
如何找到环路起始处?
现在我们知道CollisionSpot与环路起始处相距K个节点。由于K=mod(k, LOOP_SIZE)(或者换句话说,k=K+M*LOOP_SIZE,其中M为任意整数),也可以说,CollisionSpot与环路起始处相距k个节点。例如,若有个环路长度为5个节点,有个节点N处于距离环路起始处2个节点的地方,我们也可以换个说法:这个节点处于距离环路起始处7个、12个甚至397个节点。
至此,CollisionSpot和LinkedListHead与环路起始处均相距k个节点。
现在,若用一个指针指向CollisionSpot,用另一个指针指向LinkedListHead,两者与LoopStart均相距k个节点。以同样的速度移动,这两个指针会再次碰在一起——这次是在k步之后,此时两个指针都指向LoopStart,然后只需返回该结点即可。
将全部整合在一起
总结一下,FastPointer的移动速度是SlowPointer的两倍。当SlowPointer走了k个节点进入环路时,FastRunner已进入链表环路k个节点。也就是说FastRunner和SlowRunner相距LOOP_SIZE-k个节点。
接下来,若SlowPointer每走一个节点,FastRunner就走两个节点,每走一次,两者的距离就会更近一个节点。因此,在走了LOOP_SIZE-k次后,它们就会碰在一起。这时两者距离环路起始处就有k个节点。
链表首部与环路起始处也相距k个节点。因此,若其中一个指针保持不变,另一个指针指向链表首部,则两个指针就会在环路起始处相会。
根据之前的4部分描述,就能直接导出下面的算法。
(1)创建两个指针:FastPointer和SlowPointer。
(2)SlowPointer每走一步,FastPointer就走两步。
(3)两者碰在一起时,将SlowPointer指向LinkedListHead,FastPointer保持不变。
(4)以相同速度移动SlowPointer和FastPointer,一次一步,然后返回新的碰撞处。
下面给出算法代码:
LinkedListNode findBegining(LinkedListNode head) {LinkedListNode slow = head;LinkedListNode fast = head;/*找出碰撞处,将处于链表中LOOP_SIZE-k步的位置*/while(fast != null && fast.next != null) {slow = slow.next;fast = fast.next.next;if(slow == fast) { // 碰撞break;}}/*错误检查,没有碰撞处,也即没有回路*/if(fast == null || fast.next == null) {return null;}/*将slow指向首部,fast指向碰撞处,两者距离环路 *起始处k步,若两者以相同速度移动,则必定会在环 *路起始处碰在一起*/slow = head;while(slow != fast) {slow = slow.next;fast = fast.next;}/*至此两者均指向环路起始处*/return fast;}
- 给定一个有环链表,实现一个算法返回环路的开头结点
- 【Java】给定一个有环链表,实现算法返回环路的开头结点
- 9.2链表(六)——给定一个有环链表,实现一个算法返回环路的开头结点
- 程序员面试金典: 9.2链表 2.6给定有环链表,实现算法返回环路的开头节点
- 返回环路的开头结点
- 寻找有环链表的环路开头结点
- 010给定一个循环链表,实现一个算法返回这个环的开始结点 (keep it up)
- 单链表的实现(给定某结点,删除它;给定某结点,在他前面插入一个结点等)
- 给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回
- 018给定二叉查找树的一个结点, 写一个算法查找它的“下一个”结点“(keep it up)
- 有一棵二叉树,请设计一个算法,按照层次打印这棵二叉树。 给定二叉树的根结点root,请返回打印结果,
- 给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
- 判断一个单向链表是否有环路的算法
- 【Java】实现一个算法,删除单链表中间的某个结点,假定你只能访问该结点
- 请实现一个算法,确定一个字符串的所有字符是否全都不同。这里我们要求不允许使用额外的存储结构。 给定一个string iniString,请返回一个bool值,True代表所有字符全都不同,False代
- 一个敷衍的开头。。。
- CCI 2.6 寻找有环链表环路的开头节点
- cc150:实现一个算法来删除单链表中间的一个结点,只给出指向那个结点的指针
- 什么是VB进行子类化及简单介绍
- Android之ArrayAdapter详解
- HDU 2544 最短路 模板题 SPFA Dijkstra
- 关于二维数组动态申请
- ios开发之NSData
- 给定一个有环链表,实现一个算法返回环路的开头结点
- 知乎大神YaqiLYU关于tracking方向的2013-2016趋势总览!!!强烈推荐!!!
- 4.17学习流水账——GTSRB函数TrainTrafficSigns
- 【Fltk】Fltk1.3.3+VS2015 编程
- 机器学习笔记(1)-线性回归
- c++自动比较数字大小函数
- 基于django的轻量级CMS Mezzanine搭建笔记
- 单链表的创建及显示
- 如何定位消耗CPU最多的线程