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 改名为 slowl 改名为 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)) 空间。

相关推荐
致Great4 小时前
Pi 的上下文压缩,到底是怎么工作的?
算法
watersink5 小时前
机器学习聚类算法
算法·机器学习·聚类
luj_17686 小时前
桥牌思维启示:系统设计的模块化架构
c语言·开发语言·c++·经验分享·算法
饼饼学习空间智能8 小时前
家庭服务机器人训练数据怎么积累?仿真、真实采集与持续学习的技术路线分析
人工智能·算法·机器学习
蛋先生DX9 小时前
你瘦不下来但大模型可以:量化原理了解一下
深度学习·算法·llm
Scabbards_9 小时前
面试Leetcode - Heap 堆
java·leetcode·面试
牛阿大10 小时前
LQR算法
算法
(╹◡╹)11 小时前
18.剪枝
算法·机器学习·剪枝
Fa_Mian_Tuan12 小时前
图论基础|邻接矩阵超详细讲解(含无向/有向/带权图+完整可运行C语言代码)
c语言·数据结构·笔记·算法·图论