问题:给定一个有环单链表,找到链表中环的起点,也就是说,找到下图中的单链表中Join点:

(本图来源于http://www.cnblogs.com/xudong-bupt/p/3667729.html。做了少许改动)
解答:一个常见的解法是这种。声明两个指针,两个指针的初始值都是链表的头指针,当中一个指针每次前移两个节点。称为快指针,还有一个指针每次前移一个节点。称为慢指针,然后让它们两同一时候出发,由于链表中存在环,它们终于会第一次相遇。如果相遇在图中Pos点。相遇之后。慢指针从Pos点出发再往前移LenA个节点,就能到达Join点。终于得到我们的答案。
我想。大家读完上面的问题和解答后都会有一个疑问,为什么快慢指针相遇后,慢指针再往前移LenA个节点就刚好到达Join点呢?本文就是来解决大家心中的疑惑的。
网上解说利用快慢指针寻找有环单链表中环的起点算法的博文并不少,我看了从百度搜出来的前面的四五篇,还看了LeetCode上某人推荐的一个国外博文,可是没有一个把这点讲明确的。甚至有些都是错的,错的原因有两个:(1)没认识到LenA的长度可能比环的长度大,(2)没认识到两指针相遇时,快指针可能已经绕环好几圈了。扯了些废话。如今进入正题。证明分为两部分。
(一)
首先要证明的是。两指针相遇时。慢指针还没有走完整个链表。

(1)如果慢指针第一次达到Join点时,快指针也在Join点。慢指针自然没有走完整个链表;
(2)如果慢指针第一次达到Join点时。快指针没有在Join点,我们以最极端的情况来说,如果快指针这时就在慢指针的前面一个节点,这时,快指针追上慢指针须要走最长的距离。由于快指针的速度是慢指针的两倍,所以慢指针走一圈,快指针走两圈,当慢指针第一次在环上走完一圈回到Join点时,快指针刚好走完两圈。而且已经在慢指针的前面,所以它两在慢指针第一次回到Join点之前就已经相遇。
终于,得出结论:两指针相遇时,慢指针还没有走完整个链表。

(二)
然后。我们来证明。快慢指针相遇后,慢指针再往前移LenA个节点就刚好到达Join点。
如果第一次相遇点为Pos,环起点为Join。头结点到环起点的长度为LenA,环起点到第一次相遇点的长度为x,第一次相遇点到环起点的长度为y。环长为R。于是有以下结果:
(1)第一次相遇时。slow走的长度 S = LenA + x;(由证明的第一部分得到)
(2)第一次相遇时,fast走的长度 2S = LenA + n*R + x;(相遇时,快指针可能已经绕环好几圈了。至少一圈。n大于等于1。由于快指针先进入环。要追上后进入的慢指针。必须得回到环起点在起点之后才干追上)
(3)LenA + x = n*R; LenA = n*R -x;
当中,(3)是由(1)(2)推导出来的。
我们的目标是依据上面三点得出慢指针在走了S + LenA后刚好到达Join点(这是清晰说明这个问题的关键)。我们尝试依据上面三点推导出我们想要的结论:
S + LenA = S + n*R - x = S + (n - 1)*R + (R - x) = S + (n - 1)*R + y
这个表达式证明了我们的结论:慢指针在移动S + (n - 1)*R个节点后刚好在快慢指针第一次相遇的位置,再移动y个节点后就刚好达到Join点。

这里顺便给出算法的详细实现方法,以方便同学们阅读。

实现的详细流程是这种:声明两个指针。两个指针的初始值都是链表的头指针,当中一个指针每次前移两个节点,称为快指针,还有一个指针每次前移一个节点,称为慢指针,然后让它们两同一时候出发。由于链表中存在环,它们终于会第一次相遇,然后把当中一个指针移回到链表头的位置。也就是设置为链表头指针,然后让两个指针一个从相遇点出发,一个从链表头节点出发。两个都一次走一步。一直走到两个指针第一次相遇为止,这时两个指针都走了LenA的长度。以下是Java实现:

public static ListNode findLoopStart(ListNode nodeHead) {ListNode slowPointer, fastPointer;slowPointer = nodeHead;fastPointer = nodeHead;// 寻找第一次相遇点while (fastPointer != null) {fastPointer = fastPointer.next.next;slowPointer = slowPointer.next;if (fastPointer == slowPointer) {break;}}// 把慢指针移动到链表的开头slowPointer = nodeHead;while (slowPointer != fastPointer) {slowPointer = slowPointer.next;fastPointer = fastPointer.next;}return slowPointer;}
public class ListNode {public int val;public ListNode next;public ListNode(int x) {val = x;}@Overridepublic String toString() {return "ListNode [val=" + val + ", next=" + next + "]";}}

參考文章链接:
http://www.cnblogs.com/xudong-bupt/p/3667729.html
http://www.cnblogs.com/ccdev/archive/2012/09/06/2673618.html
http://blog.chinaunix.net/uid-26448049-id-3046656.html
http://learningarsenal.info/index.php/2015/08/24/detecting-start-of-a-loop-in-singly-linked-list/

证明利用快慢指针寻找有环单链表中环的起点算法相关推荐

  1. 【数据结构与算法】判断两个无环单链表是否相交的算法

    带环单链表的故事 @不了解前尘往事的Reader,烦请阅读--<判断单链表是否有环的算法> 不过我们这里不考虑带环单链表,且无环单链表相交也只是相交一次,不穿过,这是一种不算复杂的情况吧. ...

  2. 快慢指针追逐法寻找单链表中环的起点

    寻找单链表环的起点 题目 解决思路 代码 说明 题目 给定一个链表,返回链表开始入环的第一个节点. 如果链表无环,则返回 null.如下图中数字2对应的节点为环的第一个节点: 输入:head = [3 ...

  3. 利用快慢指针判断循环

    今天在leetcode刷题时,碰到一个好玩的简单题 其实这道题的题干非常简单易懂,但是涉及到了一个无限循环的可能. 从数学上来说,如何证明它是无限循环而不是无限不循环也许是一个有挑战性的问题,但是这里 ...

  4. 【数据结构与算法】带环单链表查找环的入口算法

    带环单链表的故事 @不了解前尘往事的Reader,烦请阅读--<判断单链表是否有环的算法> 如何找带环单链表的环的入口 这里只说比较可行的算法吧. 思路一:HashSet第一个重复元素就是 ...

  5. 第7周项目实践2.1 用只有尾节点指针rear的循环单链表实现队列算法库

    //用只有尾节点指针rear的循环单链表作为队列存储结构,其中每个节点的类型为LinkNode,rear指针用于唯一标识链队 typedef struct LinkNode {int date;Lin ...

  6. 【C语言】指针进阶实践(指针数组和创建单链表)

    目录 1.指针数组 2.单链表创建 1.指针数组 编写程序,输入n(n<10)个字符,输出其中最长字符串的有效长度.要求自定义函数int max_len(char *s[] , int n),用 ...

  7. 带头结点单链表就地逆置算法

    1.带头结点单链表就地逆置算法 部分函数调用参考如下:https://blog.csdn.net/qq_50504109/article/details/120288749 /*** 单向链表的逆置, ...

  8. C++线性表(单链表)的应用算法(附源码)

    C++线性表(单链表)的应用算法 线性表(单链表)的应用算法: 构造一个递增有序的正整数链表,实现链表分解为一个奇数表和一个偶数表,之后再将两个链表合并一个递减链表. 运行截图 代码实现 /* 线性表 ...

  9. 两个有序单链表的合并排序算法

    设计两个有序单链表的合并排序算法 ListList MergeList(LiskList La, LinkList Lb){Lnode *pa = La->next;Lnode *pb = Lb ...

最新文章

  1. IIS中保持HTTP连接
  2. 重磅!一文解读神策智能推荐
  3. jspServlet(2)
  4. Vue CLI 3.0脚手架如何在本地配置mock数据json
  5. linux指定cpu运行程序,进程/线程绑定到特定CPU核的linux实现(有代码有实例)
  6. Java声明字符串数组,架构师必备!
  7. jquery给div赋值
  8. 零散知识点总结(1) Gradle 使用配置总结
  9. HTMLCSS 第五天 笔记
  10. GIS_GeoTools栅格数据分析之图像变化检测
  11. 如何安装SQL2005 EXPRESS版
  12. 华硕主板破linux密码破解,华硕P8B75-M-LE老主板加持NVMe SSD bios(刷新软件和bios)...
  13. linux下nmon的安装及使用教程
  14. 周杰伦新专辑预售热点传播分析报告概览
  15. Release That Record Lock!
  16. Vue2项目引入mars3d
  17. EtherCAT运动控制卡的电子凸轮追剪飞剪等应用(一)
  18. android 日历翻页动画,Android开源库合集:轻松实现Android动态,炫目:日历效果...
  19. Don‘t make users think
  20. 【可视化分析案例】用python分析Top100排行榜数据

热门文章

  1. Keil5.15使用GCC编译器链接.a库文件
  2. 大盘过4000,注意风险~
  3. 【转】MapReduce:详解Shuffle过程
  4. ORACLE建视图 授权的 例子
  5. 30秒的PHP代码片段(2)数学 - Math
  6. LocalStorage、SessionStorage
  7. IEnumerable和IQueryable在使用时的区别
  8. 补8.python之面相对象part.7(类相关函数的补充)
  9. Android View体系(六)从源码解析Activity的构成
  10. samba服务中,关于读写(也就是下载和上传)权限的优先级