链表中是否有环?如果有找到入口结点

1.简单思路做法
首先从头节点开始,依次遍历单链表的每一个节点。每遍历到一个新节点curNode,就从头节点重新遍历新节点之前的所有节点,看是否有与curNode相同的结点。
时间复杂度O(n*n);空间复杂度O(1)
2.用HashSet存储。
建立泛型为结点类型的HashSet,用来存储曾经遍历过的结点。如果新存入的结点发现hashSet中已存在该节点。则说明链表中有环。
时间复杂度O(n)
3.快指针和慢指针
首先建立一个慢指针p1(1倍速从后移动),快指针p2(2倍速向后移动),一旦两者相遇(相等),则必定含环。
如何找到入口结点呢?
这里写图片描述
如图所示。U为入环结点,V为相遇结点,如图长度a为进入环之前的路程,b为从入环节点到相遇结点的路程,c为从相遇结点到入环结点的路程,n为环的边长。移动过程为顺时针。
假设相遇后
1.p1走过的路程为x。x=a+p*n+b (p为p1绕环的次数)
2.p2走过的路程则为2x。x=a+q*n+b (q为p2绕环的次数)
3.整理2(a+p*n+b )=a+q*n+b
4.a=(q-2p)*n-b,
由于n=b+c,
则推出a=(q-2p-1)*n+c.
5.得出结论,如果把p1放回起始结点,p2还在当前相遇结点,两者同时出发,相遇点即是入环结点。因为p1走a距离到达入环节点,同时p2走了c距离加上很多圈也到达入环节点。
由此写出代码:

 public ListNode EntryNodeOfLoop(ListNode pHead)
    {
        ListNode meetNode=findMeet(pHead);
        if(meetNode==null){
            return null;
        }
        while (meetNode!=pHead){
            meetNode=meetNode.next;
            pHead=pHead.next;
        }
        return meetNode;
    }
    public ListNode findMeet(ListNode node){
        if(node==null|| node.next==null|| node.next.next==null)return null;
        ListNode slow=node.next;
        ListNode fast=node.next.next;
        while (slow!=fast){
            if(fast.next!=null&& fast.next.next!=null) {
                slow = slow.next;
                fast = fast.next.next;
            }
//            System.out.println("slow:"+slow.val+" fast:"+fast.val);
        }
        return slow;
    }

猜你喜欢

转载自blog.csdn.net/StubbornAccepted/article/details/80423885