当前位置: 首页 > news >正文

leetcode80:删除有序数组中的重复项 II(快慢指针法)

文章目录

  • 一、 题目描述
  • 二、 核心思路:快慢指针
  • 三、计数器辅助的快慢指针
    • 代码实现-直观快慢指针
    • 优缺点分析
  • 四、 深度拓展:更通用的快慢指针模板
    • 核心思想
    • 代码实现-通用模板
    • 优缺点分析
  • 五、 总结

LeetCode 80 删除有序数组中的重复项 II,【难度:中等;通过率:63.5%】,这道题是 LeetCode 26 删除有序数组中的重复项 的进阶版,要求我们保留最多 两个重复的元素,同样可是接着使用 双指针-快慢指针法解决,并且我们通过此题,总结出一个 通用的模板,最终可以解决 删除有序数组中的重复 K 项的问题

一、 题目描述

给你一个有序数组 nums,请你原地删除重复出现的元素,使得每个元素最多出现两次,返回删除后数组的新长度

不要使用额外的数组空间,你必须在 原地 修改输入数组并在使用 O(1) 额外空间的条件下完成

示例:

输入:nums = [1,1,1,2,2,3]
输出:5, nums = [1,1,2,2,3,_]
解释:函数应返回新长度 length = 5, 并且原数组的前五个元素被修改为 1, 1, 2, 2, 3

二、 核心思路:快慢指针

解决此类问题的核心武器是快慢指针。我们将数组逻辑上分为两部分:

  • [0...slow-1]:这是我们处理好的、满足“最多重复两次”要求的新数组
  • [slow...fast-1]:这是已经被 fast 指针扫描过,但被我们“抛弃”的元素
  • [fast...n-1]:这是待探索的未知区域

指针职责:

  • slow (慢指针):作为“写入”指针,nums[slow] 是下一个将被覆盖的位置。slow 本身也代表了新数组的长度
  • fast (快指针):作为“读取”指针,nums[fast] 是当前正在考察的元素

我们的目标就是移动 fast 指针遍历整个数组,当遇到一个“合法”的元素时,就将它“写入”到 slow 指针的位置,然后 slow 前进一格


三、计数器辅助的快慢指针

一种非常直观的实现思路:通过一个额外的计数器来判断当前元素是否“合法”

代码实现-直观快慢指针

class Solution {/*** 使用快慢指针,并辅以一个 repeat 计数器来跟踪当前元素的重复次数* slow 指针永远指向下一个可以被覆盖的位置*/public int removeDuplicates(int[] nums) {if (nums.length <= 2) {return nums.length;}int slow = 1; // 新数组的构建从索引 1 开始int repeat = 1; // 记录当前元素的重复次数for (int fast = 1; fast < nums.length; fast++) {// 遇到重复元素if (nums[fast] == nums[fast - 1]) {repeat++;// 如果重复次数小于等于 2,则保留该元素if (repeat <= 2) {nums[slow++] = nums[fast];}// 如果 repeat > 2,则不移动 slow,fast 继续前进,相当于“跳过”了该元素} else { // 遇到新元素repeat = 1; // 重置计数器nums[slow++] = nums[fast]; // 保留新元素}}return slow;}
}

提交结果:

在这里插入图片描述

优缺点分析

  • 优点:逻辑非常清晰易懂,通过 repeat 计数器,我们能准确地知道每个元素的重复情况,易于理解和调试

四、 深度拓展:更通用的快慢指针模板

我们甚至可以摆脱 repeat 计数器,让逻辑更简洁,也更具扩展性,总结出一个此类题通用模板

核心思想

我们换一个角度思考:fast 指针指向的元素 nums[fast] 什么时候应该被保留?

答案是:当 nums[fast] 与新数组 [0...slow-1] 的倒数第二个元素 nums[slow-2] 不相等

为什么是 slow-2

  • nums[slow-1] 是新数组的最后一个元素
  • nums[slow-2] 是新数组的倒数第二个元素
  • 如果 nums[fast]nums[slow-2] 相等,那么 nums[slow-1] 必然也和它们相等,因为:数组有序(题目给的条件),即 nums[fast] 将是第三个重复的元素,应该被抛弃
  • 反之,如果 nums[fast]nums[slow-2] 不相等,那么 nums[fast] 最多只是第二个重复的元素,应该被保留

我们还需要处理一个边界情况:当 slow < 2 时,新数组的长度还不足 2,此时任何元素都应该被无条件保留

代码实现-通用模板

class Solution {public int removeDuplicates(int[] nums) {if (nums.length <= 2) {return nums.length;}int slow = 2; // 新数组的前两个元素 [0, 1] 默认保留for (int fast = 2; fast < nums.length; fast++) {// 检查 fast 指向的元素是否应该被保留// 如果 nums[fast] 不等于新数组的倒数第二个元素 nums[slow-2]// 说明 nums[fast] 不是第三个重复元素,可以保留if (nums[fast] != nums[slow - 2]) {nums[slow] = nums[fast];slow++;}}return slow;}
}

提交结果:

在这里插入图片描述

优缺点分析

  • 优点
    • 代码更简洁,没有额外的状态变量
    • 扩展性极强:如果题目改成“最多保留 k 个重复项”,我们只需将 2 替换为 k 即可:if (slow < k || nums[fast] != nums[slow - k])。这是一个非常强大的通用模板
  • 缺点:逻辑相对抽象,需要深刻理解 slow-2 的含义,以及为什么可以这样:题目说是有序

五、 总结

解法一 (计数器辅助)解法二 (通用模板)
核心逻辑通过 repeat 计数器判断是否保留通过与 nums[slow-2] 比较来判断是否保留
状态变量slow, fast, repeatslow, fast
代码简洁性中等
扩展性需要修改 repeat <= 2 的判断只需将 2 替换为 k 即可
时间/空间复杂度O(N) / O(1)O(N) / O(1)
http://www.dtcms.com/a/347871.html

相关文章:

  • 历史数据分析——半导体
  • 5.在云服务器上部署RocketMQ以及注意点
  • 双指针:三数之和
  • SQL注入1----(sql注入原理)
  • 深入理解 OPRF 技术:盲化伪随机函数的原理、对比与应用
  • UE 官方文档学习 C++TArray 移除操作
  • C++11: std::weak_ptr
  • 单片机的输出模式推挽和开漏如何选择呢?
  • leetcode算法刷题的第十六天
  • std::exchange详解
  • 智慧交通夜间逆光误检率↓81.4%!陌讯多模态融合算法在主干道监测的落地优化
  • 暴雨环境漏检率下降78%!陌讯动态融合算法在道路积水识别的工程突破
  • 电感反射特性
  • dinov3使用介绍
  • 【51单片机】【protues仿真】 基于51单片机储物箱系统
  • 企业智脑智能体开发全解析
  • docker的基础配置
  • SEO优化工具学习——Ahrefs进行关键词调研(包含实战)
  • 元宇宙的未来展望:机遇、风险与人类社会的新形态
  • FLOPs、TFLOPs 与 TOPS:计算能力单位
  • pig框架导入总结
  • Claude Code 新手使用入门教程
  • 第2题 - 登山鞋(C++实现)
  • 计算机组成原理(12) 第二章 - 主存储器的基本组成
  • 遥感机器学习入门实战教程|Sklearn案例⑨:数据预处理(Processing)
  • 基于STM32的智能温室控制系统设计
  • 【yocto】Yocto Project 配置层(.conf)文件语法详解
  • Service详解
  • 鸿蒙异步处理从入门到实战:Promise、async/await、并发池、超时重试全套攻略
  • 【C++】全局变量/静态变量的初始化时机