题目描述
给定一个链表的头节点
head
,返回链表开始入环的第一个节点。 如果链表无环,则返回null
。如果链表中有某个节点,可以通过连续跟踪
next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果pos
是-1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。不允许修改 链表。
示例 1:
输入:head = [3,2,0,-4], pos = 1 输出:返回索引为 1 的链表节点 解释:链表中有一个环,其尾部连接到第二个节点。示例 2:
输入:head = [1,2], pos = 0 输出:返回索引为 0 的链表节点 解释:链表中有一个环,其尾部连接到第一个节点。示例 3:
输入:head = [1], pos = -1 输出:返回 null 解释:链表中没有环。提示:
- 链表中节点的数目范围在范围
[0, 104]
内-105 <= Node.val <= 105
pos
的值为-1
或者链表中的一个有效索引进阶:你是否可以使用
O(1)
空间解决此题?
解题思路
要找出链表中环的起始节点,可以使用 Floyd 的循环检测算法(也称为 快慢指针算法),并在此基础上进一步寻找环的起始节点。解题思路
-
检测环的存在:使用快慢指针法(兔子和乌龟算法)来检测链表是否存在环。快指针每次移动两个节点,慢指针每次移动一个节点。如果链表中存在环,那么快慢指针会在环中相遇。
-
找到环的起始节点:当快慢指针相遇时,将慢指针移回链表的头部,同时保持快指针在相遇点,二者都以相同的速度(每次移动一个节点)继续移动。它们会在环的起始节点相遇。
复杂度分析
- 时间复杂度O(n): 快指针和慢指针各自遍历链表的时间复杂度为 O(n)。
- 空间复杂度O(1): 只使用了常量级别的额外空间(两个指针)。
代码实现
package org.zyf.javabasic.letcode.hot100.list;
import org.zyf.javabasic.letcode.list.base.ListNode;
/**
* @program: zyfboot-javabasic
* @description: 环形链表 II
* @author: zhangyanfeng
* @create: 2024-08-22 00:17
**/
public class DetectCycleSolution {
public ListNode detectCycle(ListNode head) {
if (head == null || head.next == null) {
return null; // 空链表或只有一个节点的链表没有环
}
ListNode slow = head;
ListNode fast = head;
// 阶段 1: 检测是否有环
while (fast != null && fast.next != null) {
slow = slow.next; // 慢指针每次移动一个节点
fast = fast.next.next; // 快指针每次移动两个节点
if (slow == fast) { // 快慢指针相遇,说明链表有环
// 阶段 2: 找到环的起始节点
ListNode entry = head; // 从头节点开始
while (entry != slow) { // entry 和 slow 都以相同速度移动
entry = entry.next;
slow = slow.next;
}
return entry; // entry 即为环的起始节点
}
}
return null; // 无环
}
public static void main(String[] args) {
DetectCycleSolution solution = new DetectCycleSolution();
// 测试用例 1: 有环的链表
ListNode head1 = new ListNode(3);
head1.next = new ListNode(2);
head1.next.next = new ListNode(0);
head1.next.next.next = new ListNode(-4);
head1.next.next.next.next = head1.next; // 尾部连接到第二个节点
System.out.println("Test Case 1: " + (solution.detectCycle(head1) != null ? solution.detectCycle(head1).val : "null")); // 应该输出 2
// 测试用例 2: 有环的链表
ListNode head2 = new ListNode(1);
head2.next = new ListNode(2);
head2.next.next = head2; // 尾部连接到第一个节点
System.out.println("Test Case 2: " + (solution.detectCycle(head2) != null ? solution.detectCycle(head2).val : "null")); // 应该输出 1
// 测试用例 3: 无环的链表
ListNode head3 = new ListNode(1);
System.out.println("Test Case 3: " + (solution.detectCycle(head3) != null ? solution.detectCycle(head3).val : "null")); // 应该输出 null
}
}
具体可参考:https://zyfcodes.blog.csdn.net/article/details/141401712