彻底删除重复节点——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 是你的最佳伙伴

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

相关推荐
️是7823 分钟前
信息奥赛一本通—编程启蒙(3395:练68.3 车牌问题)
数据结构·c++·算法
Liangwei Lin39 分钟前
LeetCode 118. 杨辉三角
算法·leetcode·职场和发展
计算机安禾41 分钟前
【c++面向对象编程】第24篇:类型转换运算符:自定义隐式转换与explicit
java·c++·算法
鼠鼠我(‘-ωก̀ )好困43 分钟前
leetGPU
算法
我星期八休息1 小时前
Linux系统编程—基础IO
linux·运维·服务器·c语言·c++·人工智能·算法
池塘的蜗牛1 小时前
A Low-Complexity Method for FFT-based OFDM Sensing
算法
故事和你912 小时前
洛谷-【图论2-1】树5
开发语言·数据结构·c++·算法·动态规划·图论
咖啡里的茶i2 小时前
视觉显著目标的自适应分割与动态网格生成算法研究
人工智能·算法·目标跟踪
paeamecium2 小时前
【PAT甲级真题】- String Subtraction (20)
数据结构·c++·算法·pat考试·pat
YL200404262 小时前
047从前序与中序遍历序列构造二叉树
算法·leetcode