环形链表问题
1. 问题描述
在一个单向链表中,某个节点的 next 指针指向了之前的某个节点,从而形成了一个环。
通常有两个问题需要解决:
- 如何判断链表是否有环?
- 如果链表有环,如何找到环的起始节点?
2. 解题思路
1. 判断链表是否有环(Floyd 判圈算法 / 快慢指针法)
- 使用 快指针
fast和 慢指针slow:slow每次走一步fast每次走两步
- 如果
fast追上slow(即fast == slow),说明链表中有环。 - 如果
fast或fast.next为空,则链表无环。
2. 找到环的起始节点(Floyd 定理)
- 当
fast和slow相遇时,slow已经走了x步,而fast走了2x步。 - 假设 环入口节点 距离链表头
L,环的长度为C,则fast和slow相遇的位置M距离环入口E还有d步。 - Floyd 定理推导出:
- 从链表头(head)出发一个新指针
ptr,从相遇点M出发一个新指针slow,二者每次走一步,最终会在环的起始节点E相遇。
- 从链表头(head)出发一个新指针
3. Java 代码实现
class ListNode {
int val;
ListNode next;
ListNode(int val) {
this.val = val;
this.next = null;
}
}
public class LinkedListCycle {
// 方法1:判断链表是否有环
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null) return false;
ListNode slow = head;
ListNode fast = head;
while (fast != null && fast.next != null) {
slow = slow.next; // 慢指针走一步
fast = fast.next.next; // 快指针走两步
if (slow == fast) { // 相遇,说明有环
return true;
}
}
return false;
}
// 方法2:找到环的起始节点
public ListNode detectCycle(ListNode head) {
if (head == null || head.next == null) return null;
ListNode slow = head;
ListNode fast = head;
ListNode entry = head; // 用于寻找环的起始节点
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) { // 快慢指针相遇
while (entry != slow) { // 另一个指针从头开始,每次走一步
entry = entry.next;
slow = slow.next;
}
return entry; // 返回环的入口
}
}
return null; // 无环
}
// 测试代码
public static void main(String[] args) {
// 构造环形链表:1 -> 2 -> 3 -> 4 -> 5 -> 3 (成环)
ListNode head = new ListNode(1);
ListNode node2 = new ListNode(2);
ListNode node3 = new ListNode(3);
ListNode node4 = new ListNode(4);
ListNode node5 = new ListNode(5);
head.next = node2;
node2.next = node3;
node3.next = node4;
node4.next = node5;
node5.next = node3; // 形成环,指向 node3
LinkedListCycle solution = new LinkedListCycle();
System.out.println("链表是否有环:" + solution.hasCycle(head));
ListNode cycleStart = solution.detectCycle(head);
System.out.println("环的起始节点:" + (cycleStart != null ? cycleStart.val : "无环"));
}
}
4. 代码解析
1. hasCycle(ListNode head)
- 功能:判断链表是否存在环。
- 实现:
- 使用快慢指针,
slow走一步,fast走两步。 - 如果
slow == fast,说明链表有环。 - 如果
fast或fast.next为空,则链表无环。
- 使用快慢指针,
- 时间复杂度:O(N),N 为链表节点数。
- 空间复杂度:O(1),只使用了两个指针。
2. detectCycle(ListNode head)
- 功能:找到环的起始节点。
- 实现:
- 先使用
hasCycle()方法判断是否有环。 - 若有环,则从
head处定义新指针entry,与slow继续移动,每次走一步。 - 当
entry == slow,即为环的起点。
- 先使用
- 时间复杂度:O(N),N 为链表节点数。
- 空间复杂度:O(1),只使用了额外指针。
5. 测试结果
运行程序,输出:
链表是否有环:true
环的起始节点:3
说明:
hasCycle正确检测到链表存在环。detectCycle正确返回了环的入口节点3。
6. 总结
- 快慢指针法 是检测链表是否有环的高效方法,时间复杂度 O(N),空间复杂度 O(1)。
- Floyd 定理 可帮助快速找到环的起始节点。
- 适用于一般的单向链表,并可扩展到
N个节点的情况。
这样,我们完整实现了 判断链表是否有环 和 寻找环的起始节点 的 Java 代码! 🚀

1725

被折叠的 条评论
为什么被折叠?



