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

1. 题目

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

题目描述

给你一个升序排列 的数组 nums,请你原地删除重复出现的元素,使每个元素只出现一次 ,返回删除后数组的新长度。

元素的相对顺序应当保持一致。

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

无需考虑数组中超出新长度后面的元素。

示例

输入:nums = 1,1,2

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

约束

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

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

  • nums 已按升序排列

2. 最佳解题思路描述(快慢双指针,最优)

  1. 快慢指针定义:

    • slow:慢指针,指向当前有效数组最后一位,初始为 0;

    • fast:快指针,遍历全部数组,逐个寻找新的不重复数字;

  2. 遍历逻辑:

    快指针遇到和 nums[slow] 不相等的元素,说明是新唯一值;slow++ 拓展有效区间,把新值覆盖到 nums[slow]

  3. 最终有效长度为 slow + 1

优势

  • 时间 (O(n)),仅一次遍历;

  • 空间 (O(1)),无 erase、无数组移位;

  • 有序数组去重通用模板,面试首选。

3. 我的可优化代码(逻辑能 AC,但性能差)

复制代码
class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        int n=nums.size();
        int m = n;
        for(int i=0;i<n-1;i++){
            if(nums[i]==nums[i+1]){
                nums.erase(nums.begin()+i);
                n--;
                i--;
                m--;
            }
        }
        return m;
    }
};

代码说明

  1. 逻辑正确性:

    发现相邻重复元素就 erase 删除,数组长度 n 同步缩减,i 回退重新判断当前位置;m 记录最终长度,能通过所有用例。

  2. 核心缺陷 & 优化点:

    • vector erase 会让删除点后所有元素整体前移,单次 erase 时间 (O(n)),嵌套循环总时间复杂度 (O(n^2)),数据量大时超时;

    • 频繁修改数组长度、回退 i,代码冗余、可读性差;

    • 额外变量 m、n 重复记录长度,无必要。

4. 最优标准代码(快慢双指针)

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

5. 总结

  1. 你的 erase 暴力删除写法可以通过测试,但时间效率极低,面试不推荐;

  2. 有序数组原地去重标准解法是快慢双指针,一次遍历无数组删除操作;

  3. 核心规律:慢指针保存唯一值末尾,快指针找新值,不同则拓展有效区间;

  4. 返回长度切记 slow + 1,slow 是下标不是长度。

6. 相关知识拓展

拓展 1:同类模板联动

LeetCode27 移除元素、26 有序去重、283 移动零共享快慢指针思想:

快指针筛选有效元素,慢指针维护原地结果数组。

拓展 2:vector erase 性能坑

vector 是连续内存,中间删除元素必须移动后方所有元素;

算法题中应尽量避免循环内频繁 erase,改用双指针覆盖赋值替代删除。

拓展 3:拓展变形(保留最多 2 个重复项 LC80)

仅微调判断逻辑,快慢指针框架不变:

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

拓展 4:复杂度对比

  1. erase 暴力写法:时间 (O(n^2)),空间 (O(1));

  2. 快慢指针最优解:时间 (O(n)),空间 (O(1))。

相关推荐
程序喵大人6 分钟前
【C++进阶】STL算法与函数对象 - 09 函数对象保存状态并复用规则
开发语言·c++·算法·stl·函数对象
致Great5 小时前
DeepSeek Harness插件开发实战教程:我让它自己写了一个 arXiv 搜索插件
算法
罗西的思考9 小时前
【Agent OS / AIOS】AOHP 深度解读:当 OS 开始为 Agent 而设计
人工智能·算法·机器学习
民乐团扒谱机10 小时前
【微实验】组合优化matlab实战(马科维茨投资模型):在收益与风险之间,寻找最优的人生配比
大数据·人工智能·算法·机器学习·数学建模·matlab·组合优化
Nil20810 小时前
leetcode 160相交链表
算法·leetcode·链表
迷途之人不知返11 小时前
算法系列2:滑动窗口
算法
Herbert_hwt12 小时前
C语言零基础入门:循环控制与数据类型详解
c语言·数据结构·算法
ZC跨境爬虫13 小时前
LeetCode 108. 将有序数组转换为二叉搜索树(递归构建详解 + Java Python 实现)
java·python·leetcode
Tisfy13 小时前
LeetCode 3090.每个字符最多出现两次的最长子字符串:二重循环 / 滑动窗口
算法·leetcode·字符串·题解·模拟·双指针·滑动窗口
-dzk-13 小时前
【技巧】LC 136.只出现一次的数字
算法·异或