news 2026/4/15 17:40:17

【小白笔记】删除排序链表中的重复元素(I 和II)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【小白笔记】删除排序链表中的重复元素(I 和II)


这道题充分利用了链表便于删除节点的特性,以及题目给出的**“已排序”**这个关键前提。


1. 解题思路:一次遍历

由于链表是已排序的,所有重复的元素在物理位置上一定是相邻的。

  1. 初始化:让一个指针cur指向head
  2. 比较与去重
    • 比较curcur.next的值。
    • 如果相等:说明遇到了重复元素。我们只需要让cur.next = cur.next.next,相当于直接把中间那个重复的节点“跳过”了(这就是链表删除节点的高效之处)。
    • 如果不相等:说明当前节点是唯一的,将cur向后移动一位。
  3. 循环终止:当curcur.next为空时,说明遍历结束。

2. 代码实现 (Python)

# Definition for singly-linked list.# class ListNode:# def __init__(self, val=0, next=None):# self.val = val# self.next = nextclassSolution:defdeleteDuplicates(self,head:Optional[ListNode])->Optional[ListNode]:# 如果链表为空或只有一个节点,直接返回ifnothead:returnhead cur=head# 只要当前节点和下一个节点都存在,就继续比较whilecurandcur.next:ifcur.val==cur.next.val:# 发现重复,删除 next 节点# 直接指向下下个节点cur.next=cur.next.nextelse:# 不重复,cur 指针后移cur=cur.nextreturnhead

3. 深度解析:为什么这题不需要dummy节点?

在上一题(反转链表 II)中,由于头节点可能会被反转/改变,我们需要dummy
但在本题中:

  • 头节点永远不会被删除。即使后面有和头节点值一样的元素,被删除的也是“后来的”重复项,第一个节点始终会被保留。
  • 既然头节点固定,我们直接返回head即可,不需要额外的虚拟节点。

4. 复杂度分析

  • 时间复杂度O(N)O(N)O(N)。我们只对链表进行了一次完整的线性扫描。
  • 空间复杂度O(1)O(1)O(1)。只使用了一个cur指针,没有开辟额外空间。


规则变了:只要一个数字重复出现过,它的所有副本都要被删除。例如:1 -> 2 -> 2 -> 3变成1 -> 3(中间的2全部消失)。


1. 核心突破:为什么要用dummy节点?

在这道题中,dummy节点是必须的
原因在于:头节点可能会被删除
例如链表是1 -> 1 -> 2,因为1是重复的,它必须被删掉。如果没有dummy,你很难处理“谁来当新的头”这个问题。有了dummy,我们只需要最后返回dummy.next


2. 解题思路:三步走

  1. 设置哨兵:创建dummy指向head,令pre指向dummypre的作用是始终指向“确定不重复的最后一个节点”。
  2. 寻找重复区间:让cur指向pre.next。如果发现cur.val == cur.next.val,说明我们进入了一个重复区域。
  3. 批量跳过:利用一个while循环,让cur一直向后移动,直到跳过所有值相同的节点。最后让pre.next = cur.next,直接把这一整段重复的“一刀切掉”。

3. 代码实现 (Python)

classSolution:defdeleteDuplicates(self,head:Optional[ListNode])->Optional[ListNode]:# 1. 只要头节点可能被删,就请出 dummydummy=ListNode(0,head)pre=dummywhilepre.nextandpre.next.next:# 检查接下来两个节点的值是否相同ifpre.next.val==pre.next.next.val:# 记录这个重复的值x=pre.next.val# 只要接下来的节点值等于 x,就不断跳过curr=pre.nextwhilecurrandcurr.val==x:curr=curr.next# 将 pre 的 next 指向第一个不等于 x 的节点pre.next=currelse:# 如果不重复,pre 才可以放心前进一步pre=pre.nextreturndummy.next

4. 关键逻辑点拨

  • 为什么pre不直接移动?
    在发现重复时,pre.next = curr之后,pre不能立即后移。因为新接上的这个curr节点,可能又是另一组重复数字的开始。必须在下一轮循环中再次检查。
  • 对比 83 题
    • 83 题:保留重复项中的一个。所以发现重复时,cur.next = cur.next.nextcur本身不动。
    • 82 题:一个不留。所以需要一个内层循环把所有val相同的都走完,然后让pre跨越式连接。

5. 复杂度分析

  • 时间复杂度O(N)O(N)O(N)。虽然有嵌套循环,但每个节点最多只被访问两次(一次由curr扫描,一次作为pre.next),依然是线性时间。
  • 空间复杂度O(1)O(1)O(1)

通过这两道题(83 和 82)的对比,感觉到dummy节点在处理“头节点可能消失”的问题时是多么好用了

whilecurrandcurr.val==x:

这行代码体现了 Python 中的两个重要概念:短路逻辑(Short-circuiting)循环过滤

1. 拆解这行代码

这行代码的作用是:“只要当前节点存在,并且它的值还是我们要找的重复值,就一直往后走。”

  • while: 开启一个循环。
  • curr: 检查curr是否为None。如果curr已经走到了链表的末尾(即空指针),这个条件就是False
  • and: 逻辑与操作。
  • curr.val == x: 检查当前节点的值是否等于我们要删除的那个重复值x

2. 为什么要加curr and ...?(防止报错)

这是这段代码最关键的地方。在链表操作中,我们经常会遇到空指针异常

如果链表的结尾正好是一串重复数字,比如1 -> 2 -> 2 -> None

  1. 第一个2curr存在,val是 2,进入循环。
  2. 第二个2curr存在,val是 2,进入循环。
  3. 关键点:此时curr移动到了None
  4. 如果没有curr and,代码会直接执行None.val == 2这时候程序会崩溃,抛出AttributeError: 'NoneType' object has no attribute 'val'

Python 的短路原则
A and B中,如果A已经是False了,Python 根本不会去看B是什么。所以先判断curr是否存在,可以安全地保护后面的.val操作不报错。


3. 这种语法的目的:批量跳过

在第 82 题中,我们要“一个不留”地删除重复项。

假设链表是:pre -> 2 -> 2 -> 2 -> 3,此时x = 2

  • curr最初指向第一个2
  • while curr and curr.val == x:会让curr像跳格子一样:
    • 跳过第一个2
    • 跳过第二个2
    • 跳过第三个2
  • 最后curr停在了3的位置。

退出循环后,我们执行pre.next = curr
效果就是:pre的下一跳直接变成了3,中间那串2整体切除了。


总结

这是一种非常经典的**“过滤重复区间”**的写法:

  1. x记住那个“坏掉”的值。
  2. while配合指针移动,把所有等于x的连续节点全部走完。
  3. 利用and的短路特性保证代码的健壮性(鲁棒性)。

这种“先检查指针是否为空,再访问指针成员”的习惯,在写任何链表或树(Tree)的代码时都是必须具备的。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/13 15:02:54

【小白笔记】无重复字符的最长子串和长度最小的子数组(滑动窗口中两种不同的“窗口控制策略)

这个问题是 “滑动窗口 (Sliding Window)” 算法的顶级经典题。 在处理“最长子串”、“子数组”等问题时,滑动窗口能够将复杂度从 O(N2)O(N^2)O(N2) 降低到 O(N)O(N)O(N)。1. 核心思路:滑动窗口 想象字符串上有一个可以伸缩的窗口: 右边界 (…

作者头像 李华
网站建设 2026/4/15 13:48:18

β-Amyloid (1-40), Rat;DAEFGHDSGFEVRHQKLVFFAEDVGSNKGAIIGLMVGGVV

一、基本信息英文名称:β-Amyloid (1-40), Rat;Amyloid β-Protein (1-40), Rat;Rat Aβ1-40中文名称:大鼠源 β- 淀粉样蛋白 (1-40);大鼠 β- 淀粉样肽 (1-40)单字母多肽序列:DAEFGHDSGFEVRHQKLVFFAEDVGSN…

作者头像 李华
网站建设 2026/4/15 13:50:06

海外回国eSIM避坑指南一定要提前搞懂,不然真的会被坑惨!

每次从海外回国,📶上网问题永远是一个焦虑源尤其是用eSIM的宝子们只要一步踩雷,真的回国第一天就寸步难行!这篇给宝子一次讲清楚:海外回国,用eSIM经常踩的坑正确避坑方式👇1️⃣回国前先确认&am…

作者头像 李华
网站建设 2026/4/14 23:43:15

Wan2.2-T2V-A14B模型部署与高保真T2V实战

Wan2.2-T2V-A14B模型部署与高保真T2V实战:从零构建专业级视频生成系统 你有没有试过这样一种场景——脑中浮现出一个极具电影感的画面:“一只机械狐狸在雪原上跃起,身后是崩塌的未来城市,闪电划破铅灰色天空”,但当你试…

作者头像 李华
网站建设 2026/4/14 1:13:42

Java 学习路线:零基础到实战,小白收藏这篇就够了

一、Java 基础(1-2 个月) (一)环境搭建与语法基础(2-3 周) JDK 安装与配置:熟练掌握 Java Development Kit(JDK)的下载、安装以及环境变量的配置,确保 Java…

作者头像 李华