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

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

相关推荐
CoovallyAIHub16 小时前
语音AI Agent编排框架!Pipecat斩获10K+ Star,60+集成开箱即用,亚秒级对话延迟接近真人反应速度!
深度学习·算法·计算机视觉
木心月转码ing18 小时前
Hot100-Day14-T33搜索旋转排序数组
算法
会员源码网21 小时前
内存泄漏(如未关闭流、缓存无限增长)
算法
颜酱1 天前
从0到1实现LFU缓存:思路拆解+代码落地
javascript·后端·算法
颜酱1 天前
从0到1实现LRU缓存:思路拆解+代码落地
javascript·后端·算法
CoovallyAIHub2 天前
Moonshine:比 Whisper 快 100 倍的端侧语音识别神器,Star 6.6K!
深度学习·算法·计算机视觉
CoovallyAIHub2 天前
速度暴涨10倍、成本暴降6倍!Mercury 2用扩散取代自回归,重新定义LLM推理速度
深度学习·算法·计算机视觉
CoovallyAIHub2 天前
实时视觉AI智能体框架来了!Vision Agents 狂揽7K Star,延迟低至30ms,YOLO+Gemini实时联动!
算法·架构·github
CoovallyAIHub2 天前
开源:YOLO最强对手?D-FINE目标检测与实例分割框架深度解析
人工智能·算法·github