一、需求
- 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回
null
。
输入:head = [3,2,0,-4], pos = 1 输出:返回索引为 1 的链表节点 解释:链表中有一个环,其尾部连接到第二个节点。
提示:
- 链表中节点的数目范围在范围
[0, 104]
内-105 <= Node.val <= 105
pos
的值为-1
或者链表中的一个有效索引
二、哈希表
2.1 思路分析
2.2 代码实现
public class Solution {
public ListNode detectCycle(ListNode head) {
Set<ListNode> hs = new HashSet<>();
ListNode p = head;
while(p != null) {
if(hs.contains(p)) {
return p;
}
hs.add(p);
p = p.next;
}
return null;
}
}
2.3 复杂度分析
- 时间复杂度为O(N);
- 空间复杂度为O(N);
三、快慢指针
3.1 思路分析
- 我们知道可以通过快慢指针判断链表是否有环,但该题要求返回入环的第一个节点,我们可以找到快慢指针相交时的节点,然后去找该节点和链表头节点有什么关系;
- 利用力扣题解中的一张图来进行推导:a表示链表头节点到入环节点的距离,b表示入环节点到快慢指针相交节点的距离;
- 当快慢指针相交后,快指针走过的距离为:a + n * (b + c) + b = a + (n + 1) * b + nc,其中n表示相交前快指针走过的圈数;
- 快慢指针位于同一起点,快指针每次移动是慢指针的两倍,故快指针走过的距离是慢指针的两倍,于是又 a + (n + 1) * b + nc = 2(a+b),得到 a = c + (n - 1) * (b + c),即从相交点到入环点的距离加上n-1倍的圈长等于链表头节点到入环点的距离;
- 因此当快慢指针相交时,定义一个指针ptr从头节点出发,它与slow一起移动,当它们相交时,就是入环节点;
3.2 代码实现
public class Solution {
public ListNode detectCycle(ListNode head) {
if(head == null) {
return null;
}
ListNode fast = head;
ListNode slow = head;
while(fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if(fast == slow) {
ListNode ptr = head;
while(ptr != slow) {
ptr = ptr.next;
slow = slow.next;
}
return ptr;
}
}
return null;
}
}
3.3 复杂度分析
- 时间复杂度为O(N),判断快慢指针是否相交,slow指针走过的距离不会超过链表长度N,相交后到入环节点,走过的距离也不会超过N,故总体时间复杂度为O(N);
- 空间复杂度为O(1),指针变量消耗常数大小的额外空间;
四、学习地址
作者:LeetCode-Solution