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)) 空间。

相关推荐
青山木2 小时前
Hot 100 --- 电话号码的字母组合
java·数据结构·算法·leetcode·逻辑回归
hanlin032 小时前
刷题笔记:力扣第206题-反转链表
笔记·leetcode·链表
闪电悠米2 小时前
力扣hot100-240.搜索二维矩阵2-单调性剪枝详解
数据结构·算法·leetcode·矩阵·哈希算法
imuliuliang2 小时前
关于基于图论的最短路径算法性能对比分析7
算法
hurrycry_小亦2 小时前
洛谷题目:P1233 [ICPC 2001 Taejon R] 木棍加工 题解(本题较难)
c++·算法·贪心算法·动态规划
imuliuliang2 小时前
关于从栈与队列看算法思维的演化路径7
算法
春生野草3 小时前
个人笔记--大顶堆和基数排序
java·数据结构·算法
白狐_7983 小时前
【408计算机网络|第04章·下|408-CN-04B】网络层(下):路由算法、RIP、OSPF、BGP、IPv6与路由器
计算机网络·算法·智能路由器
donoot4 小时前
PaddleOCR + PyMuPDF 生成【全兼容双层 PDF】完整实操指南
人工智能·算法·pymupdf·paddleocr·双层pdf