彻底删除重复节点——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,当 prev 在 1 时,发现 2 重复,删除后 prev.next 变为 3。但若链表是 1->2->2->2,删除后 prev.next 为 None,无需再判断。更重要的是,如果删除后的新 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 是你的最佳伙伴。

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

相关推荐
小超人诞生日记6 分钟前
C++ 智能指针的使用及其原理详解
c++·算法
AiNightVision3 小时前
无人机夜视模块如何选择?——从参数指标到系统集成的完整选型指南
人工智能·算法·计算机视觉·车载系统·无人机·视频·智能硬件
牧羊人.3334 小时前
卷积神经网络之VGG
人工智能·深度学习·神经网络·算法·cnn
小郎君6585 小时前
Vue 多标签页的加载态设计:为什么我没有用 Suspense 实现 PageLoading
vue.js·算法
JJJennie7775 小时前
MAI Gateway私有化部署全攻略:大模型网关如何接入?附架构图
算法·gateway·ai网关·魔芋ai
東隅已逝,桑榆非晚5 小时前
算法3.双指针法-快乐数
c++·笔记·学习·算法
专注仿真6 小时前
卷积神经网络(CNN)算法详解
人工智能·python·算法·代码·大模型训练
FII工业富联科技服务6 小时前
多智能体如何进入制造现场?从业务协同到数字孪生验证
大数据·人工智能·算法·机器人·制造·数字孪生
≮傷£≯√6 小时前
httpserver_QNetWorkClient
算法
Evand J6 小时前
【PMSM例程11】永磁同步电机(PMSM)基于UKF的转速与转子位置估计。附下载链接
算法·matlab·卡尔曼滤波·永磁同步电机·ukf·pmsm·位置估计