LeetCode82删除排序链表中的重复元素 II

文章目录

  • 删除排序链表中的重复元素 II
    • 题目描述
      • 示例
    • 核心思想
    • 最优雅解法
    • 算法步骤详解
      • 示例1演示:[1,2,3,3,4,4,5]
    • 关键理解点
      • 1. 虚拟头节点的作用
      • 2. 重复检测逻辑
      • 3. 完全删除重复节点
    • 边界情况处理
      • 情况1:空链表
      • 情况2:单节点
      • 情况3:全部重复
      • 情况4:头节点重复
    • 复杂度分析
    • 常见错误
      • 1. 忘记虚拟头节点
      • 2. 重复删除不完全
      • 3. 指针更新错误
    • 总结

删除排序链表中的重复元素 II

题目描述

给定一个已排序的链表的头 head,删除原始链表中所有重复数字的节点,只留下不同的数字。

示例

示例1:
输入:head = [1,2,3,3,4,4,5]
输出:[1,2,5]
解释:3和4都出现了重复,所以要全部删除示例2:
输入:head = [1,1,1,2,3]  
输出:[2,3]
解释:1出现了重复,所以要全部删除

核心思想

与删除重复元素I不同,这里要求完全删除重复的节点,而不是保留一个。

关键技巧:

  1. 虚拟头节点:因为头节点也可能被删除
  2. 双指针prev 指向最后一个确定保留的节点,curr 用于遍历
  3. 跳过重复:发现重复时,跳过所有相同值的节点

最优雅解法

class ListNode {int val;ListNode next;ListNode() {}ListNode(int val) { this.val = val; }ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}class Solution {public ListNode deleteDuplicates(ListNode head) {// 创建虚拟头节点,简化边界处理ListNode dummy = new ListNode(0);dummy.next = head;// prev:指向最后一个确定保留的节点// curr:用于遍历的当前节点ListNode prev = dummy;ListNode curr = head;while (curr != null) {// 检查是否有重复:curr和curr.next值相同if (curr.next != null && curr.val == curr.next.val) {int duplicateVal = curr.val;// 跳过所有值为duplicateVal的节点while (curr != null && curr.val == duplicateVal) {curr = curr.next;}// 连接prev和curr,删除所有重复节点prev.next = curr;} else {// 当前节点无重复,prev前进prev = curr;curr = curr.next;}}return dummy.next;}
}

算法步骤详解

示例1演示:[1,2,3,3,4,4,5]

初始状态:
dummy → 1 → 2 → 3 → 3 → 4 → 4 → 5 → null↑      ↑
prev   curr步骤1:检查curr(1)和curr.next(2)
1 ≠ 2,无重复
prev = curr = 1, curr = 2dummy → 1 → 2 → 3 → 3 → 4 → 4 → 5 → null↑   ↑prev curr步骤2:检查curr(2)和curr.next(3)  
2 ≠ 3,无重复
prev = curr = 2, curr = 3dummy → 1 → 2 → 3 → 3 → 4 → 4 → 5 → null↑   ↑prev curr步骤3:检查curr(3)和curr.next(3)
3 = 3,发现重复!
duplicateVal = 3
跳过所有值为3的节点:curr = 4dummy → 1 → 2 → 3 → 3 → 4 → 4 → 5 → null↑           ↑prev        curr连接:prev.next = curr
dummy → 1 → 2 ─────────→ 4 → 4 → 5 → null↑           ↑prev        curr步骤4:检查curr(4)和curr.next(4)
4 = 4,发现重复!
duplicateVal = 4  
跳过所有值为4的节点:curr = 5dummy → 1 → 2 ─────────→ 5 → null↑           ↑prev        curr连接:prev.next = curr
dummy → 1 → 2 ────────→ 5 → null↑          ↑prev       curr步骤5:检查curr(5)和curr.next(null)
curr.next = null,无重复
prev = curr = 5, curr = null最终结果:[1,2,5]

关键理解点

1. 虚拟头节点的作用

ListNode dummy = new ListNode(0);
dummy.next = head;
  • 简化边界处理,避免特殊判断头节点
  • 保证 prev 始终有效

2. 重复检测逻辑

if (curr.next != null && curr.val == curr.next.val) {// 发现重复
}
  • 通过比较相邻节点检测重复
  • 由于链表已排序,重复元素必然相邻

3. 完全删除重复节点

int duplicateVal = curr.val;
while (curr != null && curr.val == duplicateVal) {curr = curr.next;  // 跳过所有重复值
}
prev.next = curr;  // 连接到下一个不重复的节点
  • 记录重复值,跳过所有相同值的节点
  • 直接连接 prev 和第一个不重复的节点

边界情况处理

情况1:空链表

head = null
return dummy.next = null

情况2:单节点

head = [1]
curr.next = null,不进入重复检测
return [1]

情况3:全部重复

head = [1,1,1]
全部删除,prev.next = null
return []

情况4:头节点重复

head = [1,1,2,3]
虚拟头节点确保正确处理
return [2,3]

复杂度分析

  • 时间复杂度:O(n)
    • 每个节点最多被访问2次(检测+跳过)
  • 空间复杂度:O(1)
    • 只使用了常数个指针变量

常见错误

1. 忘记虚拟头节点

// 错误:头节点重复时处理复杂
ListNode prev = null;
if (head.val == head.next.val) {// 复杂的头节点删除逻辑...
}

2. 重复删除不完全

// 错误:只删除一个重复节点
if (curr.val == curr.next.val) {curr.next = curr.next.next;  // 只删除一个
}

3. 指针更新错误

// 错误:在删除重复节点后错误更新prev
prev.next = curr;
prev = curr;  // 错误!curr指向被删除的节点

总结

这个算法的精髓在于:

  1. 虚拟头节点 简化边界处理
  2. 双指针技巧 保持连接关系
  3. 完全跳过 所有重复值的节点
  4. 正确连接 prev和下一个有效节点

这是处理链表删除问题的经典模式,特别适用于需要删除多个连续节点的场景!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/921102.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/921102.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

蓝桥杯算法之基础知识(6)

目录 Ⅰ.os操作 Ⅱ.时间库(很重要) Ⅲ.基本单位换算(ms,min,h的单位换算) Ⅳ.时间戳 Ⅴ.文件读取 Ⅵ.堆 Ⅶ.math操作 Ⅷ.range()方法单独使用 Ⅸ.python 的异常输出 Ⅹ.for…

多架构/系统图,搞懂:期货账户体系,太通透了!

Hi,围炉喝茶聊产品的新老朋友好!上周和大家聊了国内6大期货交易所清算交收,感兴趣的话烦请戳蓝色链接去学习,就当为下面学习作知识铺垫,更重要是温故知新,并保持知识连贯性。另外围炉特意整理了与账户相关的文章,如下所示: “保证金被扣”拆解期货交易所:清算交收体系…

python-对图片中的头像进行抠图

要实现对图片中人脸或头像进行抠图,可以使用 Python 的 人脸检测 和 掩码生成裁剪工具。这里提供几种实现方法,用于检测图片中的人脸区域并实现裁剪效果: 方案 1: 使用 OpenCV 和 Haar级联检测人脸并裁剪 步骤 1: 安装依赖 安装 OpenCV 和其他…

OpenLayers常用控件 -- 章节一:地图缩放控件详解教程

前言在Web地图开发中,缩放控件是用户与地图交互最基本也是最重要的功能之一。OpenLayers作为功能强大的开源地图库,提供了多种缩放控件来满足不同的交互需求。本文将结合一个完整的Vue.js示例,详细介绍OpenLayers中三种主要的缩放控件&#x…

拓扑学:数学领域的魅力之钥

拓扑学:数学领域的魅力之钥 关键词:拓扑学、连续变形、同胚、流形、代数拓扑、点集拓扑、应用数学 摘要:本文深入探讨拓扑学这一现代数学的重要分支,从其基本概念到高级理论,从纯数学研究到实际应用。我们将从点集拓扑的基础开始,逐步深入到代数拓扑和微分拓扑的复杂世界…

iOS 上架 uni-app 流程全解析,从打包到发布的完整实践

uni-app 作为跨平台开发框架,凭借“一套代码,多端运行”的特性,已经成为不少团队和个人开发者的首选。 然而,很多开发者在 iOS 应用上架环节,常常遇到流程复杂、工具分散、审核繁琐等问题。 本文将以 iOS 上架 uni-app…

go 语言map是线程不安全的如何处理

在 Go 语言中,map确实是线程不安全的。当多个 goroutine 并发读写同一个 map 时,会导致 ​race condition​(竞态条件),可能引发程序崩溃或数据不一致。以下是解决方案:一、基本方案:使用互斥锁…

落地页测试case(Android视角)

落地页按钮或者adjust的链接的跳转功能和测试case(Android视角) 如果没有安装应用,跳转到应用商店的应用下载界面如果已经安装应用,跳转到应用内,再从应用内跳转到相应的页面如果落地页是在window打开,点击…

前端自动化打包服务器无法安装高版本 Node.js v22 问题解决

问题:安装高版本 node,报错。具体表现 当执行 node -v 命令时,系统提示多个 GLIBC_xxx 版本未找到,比如 GLIBCXX_3.4.21、GLIBC_2.27 等,这些是 node 程序运行所依赖的 Glibc 库的特定版本符号,当前系统安装…

shell脚本第七阶段--三剑客之awk

学习目标熟悉awk的命令行模式基本语法结构熟悉awk的相关内部变量熟悉awk常用的打印函数print能够在awk中匹配正则表达式打印相关的行一、awk介绍awk是一种编程语言,主要用于在linux/unix下对文本和数据进行处理,是linux/unix下的一个工具。数据可以来自标…

Unity 的游戏循环机制

Unity 的游戏循环机制在 Unity 中,游戏的运行是基于帧的。每一帧都遵循固定的执行顺序:处理输入执行游戏逻辑 (包括 Update、FixedUpdate 和协程)渲染场景显示帧为什么 GameTime.time 在同一帧内不变GameTime.time 是只读属性:它返回的是当前…

算法题(198):数字三角形

审题: 本题需要我们找到数字三角形中的最大路径总值,并输出 思路: 方法一:动态规划 由于本题的路径权值是路径上每一个值累加起来,问题具有阶段重复性,所以我们尝试使用动态规划解决此问题 (1&a…

变频器实习DAY42 VF与IF电机启动方式

目录变频器实习DAY42一、工作内容1.1 OF229程序重新烧录和测试二、学习内容2.1 VF与IF电机启动方式1. VF(Voltage Frequency)启动电机2. IF(Current Frequency)启动电机总结附学习参考网址欢迎大家有问题评论交流 (* ^ ω ^)变频器…

B样条曲线,已知曲线上的某个点到起点的距离,确定这个点的参数u的值的方法

B样条曲线:已知弧长 L 求参数 u 的方法1. B样条曲线定义B样条曲线由以下要素定义:控制点:P₀, P₁, P₂, ..., Pₙ节点向量( Knot Vector ):U [u₀, u₁, ..., uₘ]曲线次数:k(例如…

云计算学习100天-第44天-部署邮件服务器

目录 电子邮件通信——邮件服务器 基本功能 邮件通信的寻址 案例 网络架构 配置server服务器 电子邮件通信——邮件服务器 基本功能 为用户提供电子邮箱存储空间 处理用户发出的邮件——传递给收件服务器 处理用户收到的邮件——投递到邮箱 邮件通信的寻址 根据收件…

计算机视觉(七):膨胀操作

在计算机视觉中,膨胀是一种基本的形态学操作,主要用于处理和分析图像的形状。它通过“膨胀”或“放大”图像中的前景对象来增加其尺寸或连接断开的区域。 膨胀操作的工作原理类似于卷积,但使用的是结构元素 (structuring element)&#xff0c…

playwright+python UI自动化测试中实现图片颜色和像素对比

def compare_image(expect_path, actual_path, output_path, color_diff_threshold10.0,max_diff_pixels100):# 读取图片img1 cv2.imread(expect_path)img2 cv2.imread(actual_path)if img1.shape ! img2.shape:img2 cv2.resize(img2, (img1.shape[1], img1.shape))# ------…

企业级AI应用,Dify集成RAGFlow知识库保姆教程

第一部分:RAGFlow 端配置 在 Dify 能够调用之前,确保 RAGFlow 已经就绪并提供了可访问的 API。 步骤 1: 确保 RAGFlow 正常运行 具体可以参考:https://blog.csdn.net/qq_35354529/article/details/151149191?spm1001.2014.3001.5502 注意启动…

daily notes[9]

文章目录ubuntu notereferencesubuntu note Ubuntu can be written into a stick that boot ubuntu.the stick have the following effects. to install or upgrade Ubuntu include on macto experience the Ubuntu desktop without any actual operation in your OS.Disk Ut…

Java中 String、StringBuilder 和 StringBuffer 的区别?

在Java中,String、StringBuilder 和 StringBuffer 都用于处理字符串,但它们在可变性、线程安全性和性能上有显著区别。以下是它们的对比:1. String不可变性(Immutable)String 对象一旦创建,内容不可修改。任…