博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
《编程之美》3.6判断链表是否相交之扩展:链表找环方法证明
阅读量:6202 次
发布时间:2019-06-21

本文共 1951 字,大约阅读时间需要 6 分钟。

先看看原题:《编程之美》3.6编程判断两个链表是否相交,原题假设两个链表不带环。

注:位于(*)符号之间的文字出自于:,作者。

用指针p1、p2分别指向两个链表头,不断后移;最后到达各自表尾时,若p1==p2,那么两个链表必相交
解法

 

 扩展问题1:如果链表可能有环,上面的方法怎么调整?

分情况讨论:如果两个链表都没有环,那么同原算法;如果两个链表一个有环,一个没环,那么必然不相交。(*)如果两个链表都有环,判断一个链表环上的任一点是否在另一个链表上,如果是,则必相交,反之不相交。这时,需要找到另一个链表完整的环都包括了哪些结点,才能进行判断。(*)可以看出,解答这个问题要解决判断是否有环。
扩展问题1解法

 

扩展问题2:如果必须要求出两个链表相交的第一个节点呢?

(*)    思路:如果两个尾结点是一样的,说明它们有重合;否则两个链表没有公共的结点。    在上面的思路中,顺序遍历两个链表到尾结点的时候,我们不能保证在两个链表上同时到达尾结点。这是因为两个链表不一定长度一样。但如果假设一个链表比另一个长L个结点,我们先在长的链表上遍历L个结点,之后再同步遍历,这个时候我们就能保证同时到达最后一个结点了。由于两个链表从第一个公共结点开始到链表的尾结点,这一部分是重合的。因此,它们肯定也是同时到达第一公共结点的。于是在遍历中,第一个相同的结点就是第一个公共的结点。    在这个思路中,我们先要分别遍历两个链表得到它们的长度,并求出两个长度之差。在长的链表上先遍历若干次之后,再同步遍历两个链表,直到找到相同的结点,或者一直到链表结束。PS:没有处理一种特殊情况:就是一个是循环链表,而另一个也是,只是头结点所在位置不一样。 (*)    对于特殊情况,相交的第一个结点可以是第一个链表的表头,也可以是第二个链表的表头。因为从表头开始二者就开始相交了。对于这种情况,如果判断出二者都是循环链表,就可以直接返回其中之一的头指针。
扩展问题2解法

 

相关问题:求链表倒数第k个结点

(*)设置两个指针p1,p2,首先p1和p2都指向head,然后p2向前走k步,这样p1和p2之间就间隔k个节点,最后p1和p2同时向前移动,直至p2走到链表末尾。(*)
相关问题解法

 

现在来看,遗留问题是:1.如何判断链表有环?2.如何找到链表环的入口?方法是有的,而且这个算法在3.11节程序改错的扩展问题有所提示。简单来说就是:

对于问题1,指针p1、p2指向表头,每次循环p1指向后继,p2指向后继的后继;循环的结束条件是,p2后继为空(无环)或p1==p2(有环)。

  这个方法网上没有看到比较令我满意的解释,而《编程原本》(Elements of Programming)在第2章“变换及其轨道”中虽然有简单的说明,可当时看的时候也不是特别理解(就算现在理解了,这本书上讲得比较抽象,上面的说明放在这里只能让读者越看越糊涂),可能是悟性不够吧。一是不满足于用举例说明,二是觉得,如果是连续地移动,即像物体运动时位移和时间是连续的而不是这样离散的,当然会相遇;而二者都是离散的,如果每次都发生p2刚好越过p1的情况呢?下面用易于理解的方式证明,这个解法中如果有环,p1和p2必同时在停留在某个节点。

如左图,在任意时刻,p1和p2都在环上。由于p1每次向前1步,p2每次向前两步,用相对运动的观点来看,把p1看作静止,那么p2每次相对p1向前1步,二者在顺时针方向上的距离每经过一个时刻就减少1,直到变为0,也即二者恰好相遇。这样就证明了在离散情况下,对于有环链表,二者也是必然在某一时刻相遇在某个节点上的。

沿着这个思路,继续求解问题2寻找环的入口问题。记环的总长度为R(即,环上有R个节点),并将上述的初始时刻选为p1第一次到达环入口的时刻,相遇时p1在环上位置x%R,p2在环位置[2*(L+x)-L]%R,

在相遇点,有(2L+2x-L)%R == x%R,即(L+2x)%R==x%R。根据同余的性质,(L+x)%R==0。

此时把p1放回表头,p2的速度降为1。当p1走了L到达环的入口时,p2在环上的位置为(x+L)%R==0,这意味着p2回到了入口,且与p1相遇。并且由于之前p1不在环上,这是二者的在这一步操作后的第一次相遇,并且都在入口,这便找出了入口的位置。

重述一遍寻找环存在和环入口的方法:

用两个指针p1、p2指向表头,每次循环时p1指向它的后继,p2指向它后继的后继。若p2的后继为NULL,表明链表没有环;否则有环且p1==p2时循环可以终止。此时为了寻找环的入口,将p1重新指向表头且仍然每次循环都指向后继,p2每次也指向后继。当p1与p2再次相等时,相等点就是环的入口。

转载地址:http://lqtca.baihongyu.com/

你可能感兴趣的文章
Windows编译PHP7.2拓展
查看>>
0914 - 辛苦良久,只为这一句
查看>>
前端http协议缓存初解
查看>>
Vue教程19:Vue 2.0组件开发模式
查看>>
由集成ARouter引发的一些思考
查看>>
Git Worktree:解决分支依赖冲突的问题
查看>>
Python基础知识分享
查看>>
Android图像处理 - 高斯模糊的原理及实现
查看>>
设计一个基于vue.js 2.x的虚拟滚动条
查看>>
Android 外部存储
查看>>
高性能MySQL-笔记
查看>>
图片加载之AFNetwork(下)
查看>>
基于MaxCompute搭建社交好友推荐系统
查看>>
iOS设计模式详解
查看>>
Flutter开发实战分析-animation_demo解析导读
查看>>
利用Proxy.newProxyInstance实现AOP
查看>>
十一、关于NSTimer造成的内存泄漏和解决方法
查看>>
React Native图片缓存组件
查看>>
前后端分离——token超时刷新策略
查看>>
解放双手 | Jenkins + gitlab + maven 自动打包部署项目
查看>>