彻底删除重复节点——LeetCode 82 题「有序链表去重 II」详解

在链表操作类算法题中,删除重复元素 是一类经典问题。而 LeetCode 第 82 题《Remove Duplicates from Sorted List II》(有序链表去重 II)因其"只要重复,全部删掉"的严格要求,常被作为考察指针操作与边界处理能力的重要题目。

本文将深入剖析该问题的解题思路、关键细节,并提供清晰易懂的 Python 实现。


一、题目回顾

题目描述

给定一个已排序 的链表,删除所有存在重复数字的节点 ,只保留原始链表中没有重复出现的数字。返回修改后的链表。

示例

复制代码
输入: 1 -> 2 -> 3 -> 3 -> 4 -> 4 -> 5
输出: 1 -> 2 -> 5

输入: 1 -> 1 -> 1 -> 2 -> 3
输出: 2 -> 3

注意:与第 83 题(保留一个重复元素)不同,本题要求完全移除所有重复值的节点


二、核心难点

  1. 头节点可能被删除 → 需要使用虚拟头节点(dummy node)
  2. 连续多个相同值 → 不能仅比较相邻两个节点,需跳过整段重复区域。
  3. 边界情况多:空链表、全重复、无重复等。

三、解题思路:双指针 + 虚拟头节点

我们采用以下策略:

  1. 创建一个 dummy 节点 ,其 next 指向原链表头,用于统一处理头节点被删除的情况。
  2. 使用指针 prev(前驱)指向确定保留的最后一个节点
  3. 对于当前 prev.next 节点,检查其后是否有重复:
    • prev.next.val == prev.next.next.val,说明有重复。
    • 则用另一个指针 curr 向后遍历,跳过所有相同值的节点。
    • prev.next 直接指向第一个不同值的节点(即删除整段重复)。
    • 注意:此时不移动 prev ,因为新 prev.next 可能仍是重复段开头。
    • 若无重复,则 prev = prev.next,继续前进。

四、Python 代码实现

复制代码
# Definition for singly-linked list.
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

class Solution:
    def deleteDuplicates(self, head: ListNode) -> ListNode:
        # 创建虚拟头节点
        dummy = ListNode(0)
        dummy.next = head
        prev = dummy  # prev 指向已确认不重复的最后一个节点
        
        while prev.next and prev.next.next:
            # 如果发现重复
            if prev.next.val == prev.next.next.val:
                # 记录重复值
                duplicate_val = prev.next.val
                # 跳过所有重复节点
                curr = prev.next
                while curr and curr.val == duplicate_val:
                    curr = curr.next
                # 删除整段重复
                prev.next = curr
            else:
                # 无重复,prev 前进
                prev = prev.next
        
        return dummy.next

五、关键点解析

  • 为什么用 dummy

    因为原头节点可能被删除(如 [1,1,2]),若直接操作 head,难以返回新头。dummy 保证始终有稳定入口。

  • 为何不立即移动 prev

    例如链表 1->2->2->2->3,当 prev1 时,发现 2 重复,删除后 prev.next 变为 3。但若链表是 1->2->2->2,删除后 prev.nextNone,无需再判断。更重要的是,如果删除后的新 prev.next 本身又是重复段(如 1->2->2->3->3->4),我们必须再次检查,因此不能盲目前进。

  • 时间复杂度:O(n),每个节点最多访问两次。

  • 空间复杂度:O(1),仅用常数额外空间。


六、测试用例验证

输入 输出
[] []
[1] [1]
[1,1] []
[1,2,3,3,4,4,5] [1,2,5]
[1,1,2,2,3,3] []

均可正确处理。


七、总结

LeetCode 82 题考验的是对链表指针的精细控制和对边界条件的全面考虑。通过引入虚拟头节点谨慎移动前驱指针,我们可以优雅地解决"彻底删除重复节点"的问题。

掌握此类题型,不仅有助于面试,更能提升对链表这一基础数据结构的理解。记住:当需要删除头节点或处理复杂连接时,dummy node 是你的最佳伙伴

编程如织网,指针似针线------唯有细致穿引,方得清晰结构。

相关推荐
如意猴1 小时前
003【高精度算法】加法/减法/乘法/除法
算法
仰泳的熊猫2 小时前
题目1465:蓝桥杯基础练习VIP-回形取数
数据结构·c++·算法·蓝桥杯
Hag_202 小时前
LeetCode Hot100 15.三数之和
算法·leetcode·职场和发展
俩娃妈教编程2 小时前
洛谷选题:P1307 [NOIP 2011 普及组] 数字反转
c++·算法
laplace01232 小时前
浮点数精度
人工智能·算法·agent·qwen
blackicexs2 小时前
第四周第五天
数据结构·算法
重生之后端学习2 小时前
98. 验证二叉搜索树
java·数据结构·后端·算法·职场和发展
菜鸡儿齐2 小时前
leetcode-移动零
数据结构·算法·leetcode
紫陌涵光2 小时前
54. 替换数字(第八期模拟笔试)
数据结构·c++·算法