[编程题] nk:链表中的入环节点
题目描述
输入输出例子
无
思路
方法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)
输出:
快慢指针简便一些的代码:
//方法2:快慢指针
public ListNode EntryNodeOfLoop1(ListNode pHead){
if(pHead==null) {return null;}
ListNode slow = pHead; //慢指针
ListNode fast = pHead; //快指针
//当快指针能退出的时候,肯定就是无环咯
while(fast!=null && fast.next!=null){
//k快慢指针不同速度走
fast = fast.next.next;
slow = slow.next;
//当第一次相遇的时候,可能两并不在入环点相遇,那么此时:
if(slow==fast){
//让慢指针回首节点
slow = pHead;
while(slow!=fast){
slow = slow.next;
fast = fast.next;
}
return fast; //再碰到了就是环点
}
}
return null;
}