码迷,mamicode.com
首页 > 其他好文 > 详细

《编程之美》3.6判断链表是否相交之扩展:链表找环方法证明

时间:2014-09-22 01:03:31      阅读:387      评论:0      收藏:0      [点我收藏+]

标签:style   blog   http   color   ar   strong   div   sp   art   

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

  为了防止剧透使得没看过原题目的读者丧失思考的乐趣,我把最好的解法隐藏起来。由于这个问题本身的解答并不是本文的重点,扩展问题也采用这种形式呈现。

注:位于(*)符号之间的文字出自于:http://blog.csdn.net/v_july_v/article/details/6447013,作者v_JULY_v

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

 

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

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

 

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

bubuko.com,布布扣

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

扩展问题2解法

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

 

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

bubuko.com,布布扣
(*)设置两个指针p1,p2,首先p1和p2都指向head,然后p2向前走k步,这样p1和p2之间就间隔k个节点,最后p1和p2同时向前移动,直至p2走到链表末尾。(*)
(*)设置两个指针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必同时在停留在某个节点。

bubuko.com,布布扣

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

  沿着这个思路,继续求解问题2寻找环的入口问题。记环的总长度为R(即,环上有R个节点),并将上述的初始时刻选为p1第一次到达环入口的时刻(此时p1刚好走过L,所以p2肯定也刚走过2L),假设相遇时p1在环上位置x%R(即p1从环入口开始走刚走了x单位长度时与p2相遇),p2在环位置[2*(L+x)-L]%R,(p1刚到入口时共走了L,此时p2已走了2L,后来p1从入口开始又沿着环走了x,此时p2相应的又走了2x。因此相遇时,p2从表头起共走过2L+2x,减去无环那段即为在环上共走了L+2x)

bubuko.com,布布扣

  在相遇点,有(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每次也指向后继(即减速为1)。当p1与p2再次相等时,相等点就是环的入口。

 

 

http://www.cnblogs.com/wuyuegb2312/p/3183214.html

《编程之美》3.6判断链表是否相交之扩展:链表找环方法证明

标签:style   blog   http   color   ar   strong   div   sp   art   

原文地址:http://www.cnblogs.com/forcheryl/p/3985131.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!