[编程题] nk:链表中的入环节点

题目描述
题解参考我博客:https://www.cnblogs.com/jiyongjia/p/13359591.html
图片说明

输入输出例子

思路

方法1、借助哈希表

思想:我们通过一个dummyNode不断遍历每一个节点,当我们每次遍历到这个当前节点的时候就看他在不在哈希表中,不在的话加入进去;在的时候就恰好这个节点就是入环节点。

时间复杂度:O(n)

Java代码

import java.util.*;
/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {

    //方法1:借助哈希表;
    //思想:每次遍历一个节点,就放入到哈希表,如果再次发现哈希表中有这个节点的时候就是环的入口
    public ListNode EntryNodeOfLoop(ListNode pHead){
        //跑龙套节点
        ListNode dummyNode = pHead;
        //哈希表
        HashSet<ListNode> hashSet = new HashSet<ListNode>();
        while(dummyNode!=null){
            if(hashSet.contains(dummyNode)){
                return dummyNode;  //找到入环点
            }
            hashSet.add(dummyNode);  //否则放入哈希表
            dummyNode = dummyNode.next;
        }
        //退出循环,即为找到入环点,无环
        return null;
    }
}

输出的效率:

图片说明
方法2:快慢指针

思想:

图片说明

总结思想:

  • 第一趟跑的时候找相遇点(定义fast=phead.next; slow = phead;错开的好)
  • 找到相遇点计算环长
  • 重新定义快慢指针指向,pFast指向phead,pSlow指向pHead后的环长n的位置;同时以step=1走
  • 相遇即返回该节点为入环点。

时间复杂度O(n)

Java代码

//方法2:快慢指针(第一次快慢速度2:1找到相遇点,计算环长,第二次同步速度走,在入环点相遇)
    public ListNode EntryNodeOfLoop(ListNode pHead){
        //需要考虑只有一个节点的边界情况
        if(pHead==null) {return null;}

        //快慢指针
        ListNode pFast = pHead.next; //注意一开始让快慢指针错开,因为不错开的话,在第一次相遇点的逻辑中直接if比较返回相等。返回的就是首节点相遇
        ListNode pSlow = pHead;
        //标注是否有环
        boolean isCircle = false;
        //计算环长的计数器
        int count=1;

        //1 第一次走找是否有环和相遇点
        while(pFast!=null && pFast.next!=null){
            if(pFast==pSlow){  //在初始快慢指针赋值的时候让错开,不然在这里会返回首节点相遇的
                isCircle = true;
                break;//一定记得有环了跳出while
            }
            //否则快指针2倍速走,慢指针1倍速走
            pFast = pFast.next.next;
            pSlow = pSlow.next;
        }
        //2 检测isCircle字段看是否有环,有环的话就计算环长
        if(!isCircle){
            return null;//无环
        }else{//有环
           //2.1 让快指针从相遇点再绕环走一圈,计算环长
            pFast = pFast.next; 
            while(pFast!= pSlow){
                count++; //这里while内用的dummyFast.next,故计算的count还差最后一步。
                pFast = pFast.next;
            }

        }

        //3 我们得出了环长count值,重新把指针重新指向,快慢指针以step=1开始走,相遇就是入环点
        pFast = pSlow = pHead;
        //退出while后就是找到slow应该指向的节点的地方
        while(count>0){
            pSlow = pSlow.next;
            count--;
        }

        while(pFast!=pSlow){
            pFast = pFast.next;
            pSlow = pSlow.next;
        }
        //退出while,返回的节点就是相遇点
        return pFast;
    }

时间复杂度 :O(n)

输出:
图片说明