
本文详解链表环检测与断环的完整实现,重点解决当环起点为头节点时因 `prev` 未初始化导致的 `nullpointerexception`,通过引入虚拟头节点(dummy node)统一处理所有环位置,并提供可运行的修复代码与关键注意事项。
在链表中检测并移除环是经典算法题,其核心基于 Floyd 判圈算法(快慢指针)。但许多初学者在实现“断环”逻辑时容易忽略一个关键边界:当环的入口节点恰好是头节点(即 Head 自身在环中)时,原始的双指针同步移动逻辑会导致 prev 始终为 null,从而在 prev.next = null 处触发空指针异常。
问题根源在于原代码中 slow 和 fast 在首次相遇后,重置 slow 为 Head 并让二者同速前进以寻找环入口——但若环入口就是 Head,则循环 while(slow != fast) 一次也不执行,prev 保持初始值 null,后续赋值直接崩溃。
✅ 正确解法:引入虚拟头节点(dummy node)
通过前置一个不参与业务逻辑的 dummy 节点(数据任意,如 0),将原链表整体右移一位,使判圈和找入口过程均从 dummy 开始。这样即使环入口是原 Head,在新结构中它也变为“非首节点”,确保 prev 必然被正确赋值。
以下是修复后的完整、可运行代码:
public class loopsRemove {
public static class Node {
int data;
Node next;
public Node(int data) {
this.data = data;
this.next = null;
}
}
public static Node Head;
public static Node Tail;
public static int count = 0;
public static int removeCycle() {
// ✅ 关键修复:添加 dummy 节点,避免 head 成环时 prev 为 null
Node dummy = new Node(0);
dummy.next = Head;
Node slow = dummy;
Node fast = dummy;
boolean hasCycle = false;
// 第一阶段:检测是否存在环
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) {
hasCycle = true;
break;
}
}
if (!hasCycle) {
return 0; // 无环
}
// 第二阶段:找到环入口的前驱节点(即要断开的位置)
slow = dummy;
Node prev = null;
while (slow != fast) {
prev = fast; // prev 始终指向 fast 的前一节点(在同速移动中可靠更新)
slow = slow.next;
fast = fast.next;
}
// ✅ 此时 prev 必然非 null,安全断环
prev.next = null;
return 1;
}
public static void main(String[] args) {
// 构造带环链表:3→4→5→6→3(环入口为 Head)
Head = new Node(3);
Head.next = new Node(4);
Head.next.next = new Node(5);
Head.next.next.next = new Node(6);
Head.next.next.next.next = Head; // 形成环
System.out.println(removeCycle()); // 输出:1
// ✅ 安全验证:打印 next 引用(而非 .data),避免对 null 调用 data
System.out.println(Head.next.next.next.next); // 输出:null
}
}? 关键注意事项:
- 永远不要对可能为 null 的引用调用 .data:原代码 System.out.println(Head.next.next.next.next.data) 在断环后该节点 next 已为 null,会再次抛出 NPE;应改为打印引用本身(如 ...next)或先判空。
- dummy 是局部变量,作用域仅限于 removeCycle() 内部:它不会污染原链表结构,方法返回后即符合 GC 条件,无需手动清理。
- count 变量在此场景中未被使用:建议移除或明确用途,避免误导。
- 该方案时间复杂度仍为 O(n),空间复杂度 O(1),满足最优要求。
通过虚拟头节点技巧,我们优雅地消除了边界特例,使环检测与断环逻辑具备完全鲁棒性——无论环入口在何处,都能安全、准确地完成修复。










