勉强的猫
博客园 首页 新随笔 联系 订阅

环形链表 II (Linked List Cycle II) 解题思路

原题目在https://leetcode-cn.com/problems/linked-list-cycle-ii/description/,这里粘一张图片:

 

这里为了满足不用额外空间的要求,一般采用链表操作的双指针技巧,也就是使用快慢指针的方式进行解题。

参考了很多博客和网页,大致有以下两种思路:

  

  1、快慢指针相遇时,让其中一个指针先走一圈数出环的节点个数,再让头指针先走环长度的步数,一个临时指针从原来头指针位置开始走,

             这样头指针比这个临时指针先走一个环的步数,它们每个阶段分别只走一步,相遇时侯自然是环的入口点

  

  2、快慢指针相遇时,让其中一个指针与头指针一起一步一步地走,相遇时侯就是环的入口点。

 

第一个思路清晰明了,第二个思路很巧,所以想证明第二个思路。严谨的数学证明想半天没出来,所以就画图说明吧:

如图,不妨先考虑环比较大的情况下,

快慢指针在G处相遇,则可以认为快指针第二次到达G(环比较大,只绕了一圈),而慢指针第一次达到G点。

或者我们认为在慢指针达到G时快指针到达G后又整整绕了一圈(快指针速度是慢指针的两倍),也就是说慢指针走过的距离正好是环的长度的一倍(即相等)

若将慢指针走过的节点重新组织成类似环的样子,如图所示:

不严谨的说,除了末尾外这两个图是同构的。那么当两个节点分别从G和A出发时就会在第一个 两环同一位置都相同的节点 B处(入口处)相遇。

 

另外一个是考虑环比较小的情况,

此时快指针饶了很多圈到达G点与慢指针会合,不难想到有慢指针走过的距离是环的长度的n倍(也就是说慢指针走过的距离整除于环的长度

仿照上面的证明方式,就不难得出结论了,无非一个是小环,一个是大环。或者你可以想象一个小环在大环中(大环不动)顺时针滚动的样子(方式GA-EB-FC-GD-EE-FF)

 滚动时第一个相同节点E就是入口处

下面贴出代码,注释部分是第一个思路需要的代码。

public class Solution {
    public ListNode detectCycle(ListNode head) {
        if(head==null || head.next==null)return null;
        ListNode q = head;
        ListNode s = head;
        while(q!=null && q.next != null){
            q = q.next;
            q = q.next;
            s = s.next;
            if (q==s)break;
        }
        if(q==null || q.next==null)return null;
//        int step = 1;
//        q = q.next;
//        while(q!=s){
//            step++;
//            q = q.next;
//        }
//        q = head;
//        while(step-->0)head=head.next;
        while(q!=head){
            q=q.next;
            head=head.next;
        }
        return q;
    }
}

 

posted @ 2018-08-24 16:32  勉强的猫  阅读(1528)  评论(0编辑  收藏  举报