LeetCode 80 删除有序数组中的重复项 II

1. 题目

80. 删除有序数组中的重复项 II - 力扣(LeetCode)

题目描述

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

不要使用额外的数组空间,必须在 (O(1)) 额外空间并原地修改输入数组。

元素的相对顺序应该保持一致。无需考虑数组超出新长度后的部分。

示例

输入:nums = 1,1,1,2,2,3

输出:5,nums = 1,1,2,2,3,* 输入:nums = 0,0,1,1,1,1,2,3,3 输出:7,nums = 0,0,1,1,2,3,3,*,_

约束

  • (1 <= nums.length <= 3*10^4)

  • (-10^4 <= numsi <= 10^4)

  • nums 已按升序排列

2. 最佳解题思路描述(快慢指针通用模板,本题最优)

  1. 核心规则:有序数组允许同一个数字最多保留 2 个;

  2. 快慢指针定义:

    • r(慢指针):下一个可填充的有效位置,初始为 2(前两个数字天然合法,直接保留);

    • l(快指针):遍历全部数组,从下标 2 开始向后扫描;

  3. 判断逻辑:

    对比当前快指针值 nums[l] 和有效区间倒数第二个值 nums[r-2]:

    • 不相等:说明当前数字出现次数≤2,是合法元素,覆盖到 nums[r],慢指针右移;

    • 相等:说明当前数字已经存在两个,直接跳过;

  4. 遍历结束后 r 即为有效数组长度,直接返回。

优势

  • 时间复杂度 (O(n)),仅单次遍历;

  • 空间复杂度 (O(1)),原地赋值,无 erase、无额外容器;

  • 可拓展通用:最多保留 k 个重复项,只需把初始 r、对比下标改成 k。

3. 我的可优化代码(逻辑完全正确,是标准最优解,仅微小细节优化)

复制代码
class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        int n=nums.size();
        if(n<2){
            return n;
        }
        int r = 2;
        for(int l = r;l<n;l++){
            if(nums[l]!=nums[r-2]){
                nums[r++]=nums[l];
            }
        }
        return r;
    }
};

代码说明

  1. 正确性:逻辑无任何 bug,能通过全部测试用例,是行业标准最优写法;

  2. 可优化小细节:

    • 边界判断 if(n<2) 可以省略:数组长度 0/1 时,for 循环不会执行,直接 return r=2 会出错,因此该判断必须保留;

    • 变量名语义优化:r 改名为 slow、l 改名为 fast,和 26、27 题命名统一,方便记忆;

    • 循环简写:nums[slow++] = nums[fast] 压缩一行。

4. 标准规范版代码(统一快慢指针命名)

复制代码
class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        int n = nums.size();
        if (n <= 2) return n;
        int slow = 2;
        for (int fast = 2; fast < n; fast++) {
            if (nums[fast] != nums[slow - 2]) {
                nums[slow++] = nums[fast];
            }
        }
        return slow;
    }
};

5. 总结

  1. 你的代码思路、实现完全正确,是本题最优解法,面试可直接书写;

  2. 核心判断记忆点:最多保留 2 个,就和 slow-2 对比;

  3. 初始慢指针从 2 开始:前两个数字一定合法,无需校验;

  4. 有序数组限制是该解法成立的前提,无序数组不能使用此模板;

  5. 无 erase、无嵌套循环,性能远优于暴力删除方案。

6. 相关知识拓展

拓展 1:通用模板 ------ 最多保留 k 个重复元素

将本题逻辑泛化,任意 k 都适用:

复制代码
int removeDuplicates(vector<int>& nums, int k) {
    int n = nums.size();
    if (n <= k) return n;
    int slow = k;
    for (int fast = k; fast < n; fast++) {
        if (nums[fast] != nums[slow - k]) {
            nums[slow++] = nums[fast];
        }
    }
    return slow;
}
  • LC26(最多保留 1 个):k=1,slow 初始 = 1,对比 slow-1;

  • LC80(最多保留 2 个):k=2,slow 初始 = 2,对比 slow-2。

拓展 2:暴力 erase 对比(不推荐)

循环 erase 删除第三个及以后重复数字,时间复杂度 (O(n^2)),大数据超时:

复制代码
int removeDuplicates(vector<int>& nums) {
    for(int i=2;i<nums.size();i++){
        if(nums[i]==nums[i-2]){
            nums.erase(nums.begin()+i);
            i--;
        }
    }
    return nums.size();
}

拓展 3:同类快慢指针题目汇总

  1. LC27 移除指定元素;

  2. LC26 有序数组去重(最多 1 个);

  3. LC80 有序数组去重(最多 2 个);

  4. LC283 移动零;

    全部遵循:fast 遍历筛选,slow 维护有效数组。

拓展 4:复杂度对比

  • 快慢指针最优解:(O(n)) 时间,(O(1)) 空间;

  • erase 暴力删除:(O(n^2)) 时间,(O(1)) 空间。

相关推荐
tangguofeng1 小时前
基于分治策略的循环赛日程表问题的思路讲解
算法
起个名字费劲死了1 小时前
VisionMaster集成深度学习算法(基础狗)
人工智能·深度学习·算法
lengxuenong1 小时前
枚举例题分析
c++·算法·青少年编程
Navigator_Z2 小时前
LeetCode //C - 1269. Number of Ways to Stay in the Same Place After Some Steps
c语言·算法·leetcode
2601_965742222 小时前
短视频脚本创作方法分享:本地生活类账号的起步思路
大数据·人工智能·算法·ai·新媒体运营·生活
55873生态系统手记2 小时前
读完 55873 资产钱包的技术栈,我重新理解了 "合规钱包" 应该长什么样
算法·架构
旋生万物2 小时前
素数螺旋映射 $z_n=n^{1+i}$ 的角分布统计检验与零模型对比
大数据·前端·人工智能·算法·云原生·螺旋生成论·螺旋相位
HRTOS2 小时前
HRTOS Shell继续完善:优化printf缓冲区与字符串越界保护,国庆后随新版本更新
运维·单片机·嵌入式硬件·算法·51单片机
邵奈一2 小时前
当沉香之乡遇上 Seed-2.1-pro:我用 AI 给村子做了一个数字门面
算法·架构
海天一色y3 小时前
图像分割全解析:从经典算法到深度学习的原理与实战(Python + MATLAB)
python·深度学习·算法