描述
给一个长度为n链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null
。数据范围: n≤1000
。要求:空间复杂度 O(1)
,时间复杂度O(n)
输入分为2段,第一段是入环前的链表部分,第二段是链表环的部分,后台将这2个会组装成一个有环或者无环单链表;返回链表的环的入口结点即可。而我们后台程序会打印这个节点
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
示例
input1: {1,2},{3,4,5}
output1: 3
note: 返回环形链表入口节点,我们后台会打印该环形链表入口节点,即3
input2: {1},{}
output2: "null"
note: 没有环,返回null,后台打印"null"
input3: {},{2}
output3: 2
note: 只有环形链表节点2,返回节点2,后台打印2
思路
如果不考虑时间复杂度,本题可以直接暴力破解:遍历链表并记录结点出现的次数;由于需要时间复杂度为O(1)
,因此本题只能寻找数学规律(解法2)。
1、暴力破解法
直接遍历整个链表,直到结点为null
或者出现2
次,在本方法种可以直接使用HashMap
或者HashSet
。注意:空间复杂度为O(n)
import java.util.*;
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead) {
HashMap<ListNode, Integer> map = new HashMap<>();
while(pHead != null){
//结点不存在,则存入;否则表示结点重复
if(map.get(pHead) == null) map.put(pHead, 1);
else return pHead;
pHead = pHead.next;
}
return null;
}
}
2、寻找数学规律
假设有两个指针:慢指针每次移动一步,快指针每次移动两步,如果链表有环,则两指针同时从起点出发必定会在环中相遇。我们可以设链表的起点到环入口的距离为A
,环入口沿着链表的方向到相遇点的距离为B
,相遇点沿着链表的方向到环入口的距离为C
,环的总长度为B+C
根据两指针的速度关系,可知:当两指针第一次相遇时,移动距离具有以下关系(n > 0
是快指针在环内移动的圈数):
2*(A+B) = A+B+n*(B+C)
==> A = (n-1)(B+C) + C
由上述关系可知,当两指针第一次相遇时,慢指针从链表起点开始,快指针从相遇点开始,两指针以每次移动一步的速度同手移动,则两指针必定在环入口处相遇。
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead) {
//此处的判断可要可不要
if(pHead == null || pHead.next == null) return null;
ListNode slow = pHead, fast = pHead;
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
//相遇则证明链表有环
if(slow == fast){
slow = pHead;
while(slow != fast){
slow = slow.next;
fast = fast.next;
}
return slow;
}
}
return null;
}
}