描述
给一个长度为n链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。
数据范围: n\le10000n≤10000,1<=结点值<=100001<=结点值<=10000
要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)
例如,输入{1,2},{3,4,5}时,对应的环形链表如下图所示:
可以看到环的入口结点的结点值为3,所以返回结点值为3的结点。
输入描述:
输入分为2段,第一段是入环前的链表部分,第二段是链表环的部分,后台会根据第二段是否为空将这两段组装成一个无环或者有环单链表
返回值描述:
返回链表的环的入口结点即可,我们后台程序会打印这个结点对应的结点值;若没有,则返回对应编程语言的空结点即可。
1、HashSet 76ms 14092KB
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead) {
HashSet<ListNode> set = new HashSet<ListNode>();
while (pHead!=null){
if (set.contains(pHead)){
return pHead;
}else{
set.add(pHead);
pHead = pHead.next;
}
}
return null;
}
}
2、双指针
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead) {
//1、判断链表中有环
if(pHead==null)
return null;
ListNode p1 = pHead;
ListNode p2 = pHead;
boolean flag = false;
//寻找相遇点
//循环条件考虑到{1},{}的情况,避免越界访问
while(p2!=null&&p2.next!=null){
p1 = p1.next;
p2 = p2.next.next;
if (p1==p2){
flag = true;
break;
}
}
if (flag){
//计算环的节点数
p2 = p2.next;
int n = 1;
while (p1!=p2){
n++;
p2 = p2.next;
}
//p1先走环节点数个节点,然后两指针一起走,直到相遇,相遇点为入口节点
p1 = p2 = pHead;
for(int i=0;i<n;i++){
p1 = p1.next;
}
while(p1!=p2){
p1 = p1.next;
p2 = p2.next;
}
return p1;
}else{
return null;
}
}
}
3、计算
已知:
f指针的速度为s指针的2倍,两指针运动时间相等。
t(f)=(a+b)/1
t(s)=\[(a+b)+n(b+c)\]/2
得:
t(f)=t(s)
(a+b)/1=\[(a+b)+n(b+c)\]/2
2(a+b)=(a+b)+n(b+c)
a+b=n(b+c)
a=n(b+c)-b-c+c
a=(n-1)(b+c)+c
由a=(n-1)(b+c)+c可知,当n=1时a=c,当n>1时a=n圈+c,
即两指针分别从链表头节点和相遇点同时同速度前进,最终会在环入口处相遇。
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead) {
//1、判断链表中有环
if(pHead==null)
return null;
ListNode p1 = pHead;
ListNode p2 = pHead;
boolean flag = false;
//寻找相遇点
//循环条件考虑到{1},{}的情况,避免越界访问
while(p2!=null&&p2.next!=null){
p1 = p1.next;
p2 = p2.next.next;
if (p1==p2){
flag = true;
break;
}
}
if (flag){
p1 = pHead;
while(p1!=p2){
p1 = p1.next;
p2 = p2.next;
}
return p1;
}else{
return null;
}
}
}