本文详解 LeetCode #83 题「删除排序链表中的重复元素」的常见逻辑陷阱,重点剖析 head != head.next 这一无效判据的本质问题,并提供两种可靠实现(哨兵节点法与原地修改法),附带边界处理、代码优化与执行逻辑说明。
本文详解 leetcode #83 题「删除排序链表中的重复元素」的常见逻辑陷阱,重点剖析 `head != head.next` 这一无效判据的本质问题,并提供两种可靠实现(哨兵节点法与原地修改法),附带边界处理、代码优化与执行逻辑说明。
在解决 LeetCode 第 83 题「删除排序链表中的重复元素」时,一个典型错误是误用引用比较代替值比较,例如写成 if (head != head.next)。该条件始终为 true——因为 head 和 head.next 是两个不同对象的引用(除非链表成环,但题设为普通单链表),根本无法检测数值重复。真正需要判断的是节点值是否相等:head.val == head.next.val,且必须前置校验 head.next != null,否则将触发 NullPointerException。
下面以两种主流解法展开说明:
✅ 方法一:哨兵节点 + 双指针构建新链(推荐用于理解逻辑)
该方法不修改原链结构,而是构建一条无重复的新链。关键在于:
- 仅当当前节点与下一节点值不同时才接入结果链;
- head 每轮必须前移(head = head.next),而 result 仅在保留节点时移动;
- 处理尾节点:当 head.next == null 时,当前节点必为最后一个唯一值,需无条件保留。
public ListNode deleteDuplicates(ListNode head) {
if (head == null) return null;
ListNode dummy = new ListNode(-1); // 哨兵节点
ListNode result = dummy;
while (head != null) {
// 当前节点是末尾,或与下一节点值不同 → 保留
if (head.next == null || head.val != head.next.val) {
result.next = head;
result = result.next; // 仅此时推进 result
}
head = head.next; // head 必须每轮推进
}
result.next = null; // 断开可能残留的引用(可选但更严谨)
return dummy.next;
}
⚠️ 注意事项:
- 切勿在 if 外写 result = result.next,否则会将重复节点也纳入结果;
- result.next = head 后未置空,若原链后续有冗余连接,可能引发意外输出(故建议结尾显式断链);
- 时间复杂度 O(n),空间复杂度 O(1)(仅额外常数变量)。
✅ 方法二:原地修改(空间最优,经典解法)
直接在原链上跳过重复节点,通过 temp.next = temp.next.next 实现“删除”效果:
public ListNode deleteDuplicates(ListNode head) {
ListNode temp = head;
while (temp != null && temp.next != null) {
if (temp.val == temp.next.val) {
temp.next = temp.next.next; // 跳过重复节点
} else {
temp = temp.next; // 仅当不重复时移动指针
}
}
return head;
}
✅ 优势:无需额外节点,完全原地操作;
❗ 关键点:while 条件中 temp.next != null 保证 temp.next.val 安全访问;内层 if 不改变 temp 位置,持续跳过所有连续重复值(如 [1,1,1,2] 中第一个 1 会连续跳过后续两个 1)。
? 错误代码复盘
原始失败代码的核心缺陷:
if (head != head.next) { // ❌ 引用恒不等,条件永远成立
result.next = head;
head = head.next; // ❌ 此处移动 head,导致 result 未更新就赋值
}
result = result.next; // ❌ 无论是否重复都推进 result → 复制全部节点
这使得算法既未识别重复,又错误扩展了结果链,最终输出原链。
总结:链表去重的本质是值比较 + 安全边界检查 + 指针精准控制。避免凭直觉使用引用比较,牢记 null 检查前置,区分「遍历推进」与「结果追加」两个独立动作。掌握这两种解法,不仅能通关 LeetCode #83,也为处理更复杂的链表去重(如保留一个副本、删除所有重复项)打下坚实基础。











