题目
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
说明:不允许修改给定的链表。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:tail connects to node index 1
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:tail connects to node index 0
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:no cycle
解释:链表中没有环。
进阶:
你是否可以不用额外空间解决此题?
解答
解法一:记录遍历情况
这种解法跟 Leetcode 141. 环形链表 的 解法一 类似。
具体可以看我的上一篇博客:https://blog.csdn.net/LetJava/article/details/96158308
复杂度分析:
- 时间复杂度受到频繁的 trace.contains 操作的影响,实际时间复杂度 >= O(n)
- 空间复杂度 O(n)
优点是简单,缺点是效率不行。
代码
/**
* 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) {
Set<ListNode> trace = new HashSet<>();
ListNode p = head;
while(p != null) {
if(trace.contains(p)) return p;
trace.add(p);
p = p.next;
}
return null;
}
}
结果
解法二:先计算环长,再逐个查找
具体步骤如下:
- 首先计算出环的长度 n 。
- 然后从 head 出发( p = head),向后遍历 n 个结点,如果等于当前结点,说明是环点,直接返回 p 。
- 否则 p = p.next ,继续遍历。
- 没有环就返回 null 。
复杂度:O(n^2) 的时间,O(1)的空间
缺点:时间复杂度不行,太高了。
代码
/**
* 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 fast = head;
ListNode slow = head;
int n = 0; // 环的长度
while(fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if(slow == fast) {
fast = fast.next;
for(n = 1; fast != slow; fast = fast.next) {
n ++;
}
break;
}
}
if(n != 0) {
for(ListNode p = head; p != null; p = p.next) {
ListNode q = p;
for(int i = 0; i < n; i ++) {
q = q.next;
if(q == p) return p;
}
}
}
return null;
}
}
结果
解法三:Floyd 算法
没错,是一个需要推导的数学题 :)
通过一系列推导得出结论:快慢指针相遇点到入环点的距离 = 头结点到入环点的距离
推导过程参见 Leetcode 官方题解:leetcode 官方题解传送门
复杂度:O(n) 的时间, O(1) 的空间 。
代码
/**
* 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;
ListNode fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) {
slow = head;
while (slow != fast) {
slow = slow.next;
fast = fast.next;
}
return slow;
}
}
return null;
}
}