题目描述:
给定一个链表,返回链表开始入环的第一个节点。如果链表无环,则返回 null
。
为了表示给定链表中的环,我们使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos
是 -1
,则在该链表中没有环。
注意,pos
仅仅是用于标识环的情况,并不会作为参数传递到函数中。
说明:不允许修改给定的链表。
进阶:你是否可以使用 O(1) 空间解决此题?
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。
提示:
- 链表中节点的数目范围在范围 [0, 1 0 4 10^{4} 104] 内
- - 1 0 5 10^{5} 105 <= Node.val <= 1 0 5 10^{5} 105
- pos 的值为 -1 或者链表中的一个有效索引
题目分析:
这道题是关于链表找环路的问题,这种题有一个通用的解法,即采用快慢指针法(也称Floyd判圈法)求解。给定两个快慢指针,分别命名为 slow 和 fast,起始位置都指向链表的开头。每次 fast 前进两步,slow 前进一步。如果 fast可以走到尽头,那么说明没有环路;如果 fast 可以无限走下去,那么说明一定有环路,且一定存在一个时刻 slow 和 fast 相遇。这个第一次相遇很好理解。当 slow 和 fast 第一次相遇时,我们将 fast 重新移动到链表开头,并让 slow 和 fast 每次都前进一步。当 slow 和 fast 第二次相遇时,相遇的节点即为环路的开始点。但为什么一定会发生第二次相遇且相遇点就是环路开始的点呢?
我们假设慢指针slow走了n步,快指针fast经过了一圈走了2n步它们刚好第一次相遇。那么第一次相遇的时候快指针与慢指针的行程差是一个环的长度(如图)。
设相遇点距环的起点的距离为m步,那么环的起点距头结点head的距离为n-m步,即慢指针从head出发行走n-m步即可到达我们所求的环起点。巧的是一个环的长度刚好为n步,所以从第一次相遇点到环的起点刚好也为n-m步,所以,只要一个指针从head开始走n-m步,同时另一个指针从第一次相遇点沿着环走n-m步刚好能在环的起点相遇。
题解一:
执行用时: 0 ms
内存消耗: 38.7 MB
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
// 定义两个指针,同时指向链表头
ListNode slow = head, fast = head;
// 判断是否存在环路
do {
// 如果快指针遇到为空的情况,说明不存在回路
if (fast == null || fast.next == null)
// 返回空
return null;
// 快指针每次前进2步
fast = fast.next.next;
// 慢指针每次前进1步
slow = slow.next;
// 当快慢指针第一次相遇,证明有回路,跳出循环
} while (fast != slow);
// 快指针指向链表头
fast = head;
// 寻找环路节点,当快慢指针第二次相遇,证明此相遇节点为环路的开始点
while (fast != slow) {
// 慢指针每次前进1步
slow = slow.next;
// 快指针每次前进1步
fast = fast.next;
}
// 返回入环的第一个节点
return fast;
}
}
题解二:
执行用时: 1 ms
内存消耗: 37.6 MB
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
// 如果节点只有0个或者1个,根本不可能成环,如果有三个节点,如果第三个节点为null,则没成环
if(head == null || head.next == null || head.next.next == null) {
return null;
}
// 初始化慢指针为head.next
ListNode slow = head.next;
// 初始化快指针为head.next.next
ListNode fast = head.next.next;
// 当慢指针没追上快指针
while(slow != null && fast != null && fast.next != null && slow != fast) {
// 慢指针移动一步
slow = slow.next;
// 快指针移动两步
fast = fast.next.next;
}
fast = head;
// 将fast返回head,和slow同步进行移动,最后两者相等时则为入环点
while(slow != null && fast != null && fast != slow) {
fast = fast.next;
slow = slow.next;
}
return slow;
}
}
题目来源:力扣(LeetCode)