LeetCode 27 移除元素

1. 题目

27. 移除元素 - 力扣(LeetCode)

题目描述

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

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

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例

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

输出:2, nums = 2,2,*,*

输入:nums = 0,1,2,2,3,0,4,2, val = 2

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

约束

  • (0 <= nums.length <= 100)

  • (0 <= numsi <= 50)

  • (0 <= val <= 100)

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

  1. 定义快慢指针:

    • fast 快指针:遍历整个数组,逐个检查当前元素;

    • slow 慢指针:指向有效数组待填充位置,记录不等于 val 的元素存放下标;

  2. 遍历逻辑:

    • nums[fast] != val:说明是保留元素,把值赋给 nums[slow],慢指针后移;

    • 若等于 val:直接跳过,慢指针不动;

  3. 遍历结束后,slow 的数值就是新数组长度,直接返回。

优势

  • 时间复杂度 (O(n)):仅一次遍历数组;

  • 空间复杂度 (O(1)):仅两个临时变量,原地修改,无额外容器;

  • 代码极简、逻辑清晰,是数组原地去重 / 移除指定值通用模板。

3. 我的可优化代码(当前代码无逻辑错误,是标准最优解,仅补充优化小细节)

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

代码说明

  1. 正确性:代码逻辑完全正确,能通过全部测试用例,属于本题最优写法,无逻辑 bug;

  2. 可优化小细节(不影响 AC,仅优化拷贝操作):

    当前写法存在不必要赋值:当 slow == fast 时(前面没有待删除元素),nums[slow] = nums[fast] 是自身赋值,可加判断跳过:

    复制代码
    if (val != nums[fast]) {
        if(slow != fast) nums[slow] = nums[fast];
        slow++;
    }

    数组全为保留元素时减少重复赋值,性能微提升。

4. 最优标准代码(你原版,行业通用最简版)

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

5. 总结

  1. 快慢双指针是原地移除元素核心模板,本题代码无错误、效率最优,面试直接写;

  2. 核心规则:快指针负责检索,慢指针负责维护有效数组;

  3. 返回值是慢指针最终数值,不需要额外计算长度;

  4. 题目不要求尾部元素清零,无需额外操作。

6. 相关知识拓展

拓展 1:同类快慢指针数组题型模板

  • LeetCode 26 删除有序数组重复项、LeetCode 283 移动零,完全复用快慢指针思想:

    快指针遍历筛选有效元素,慢指针存放结果。

拓展 2:另一种解法(首尾双指针,适合删除元素很多的场景)

左右指针,左找待删除值、右找保留值,交换后收缩区间,减少数组赋值次数:

复制代码
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int left = 0, right = nums.size() - 1;
        while(left <= right){
            if(nums[left] == val){
                swap(nums[left], nums[right]);
                right--;
            }else{
                left++;
            }
        }
        return left;
    }
};

特点:会打乱数组原有顺序,题目允许顺序改变时可用。

拓展 3:复杂度对比

  1. 快慢指针(你的写法):(O(n)) 时间,(O(1)) 空间,保留原数组顺序;

  2. 首尾交换双指针:(O(n)) 时间,(O(1)) 空间,数组顺序打乱;

  3. 暴力 erase:嵌套循环 erase,(O(n^2)) 时间,效率极低,不推荐面试。

拓展 4:快慢指针通用记忆口诀

快指针全遍历,慢指针存有效;

不等目标就留存,慢指针自增记长度。

相关推荐
苦瓜小生15 分钟前
【前端】【力扣与手撕】十天带你刷完前端算法与手撕,全是最简单好记的最优解法!day4
前端·数据结构·算法·leetcode·面试
不会就选b29 分钟前
算法日常・每日刷题--<贪心>5
算法
圣保罗的大教堂38 分钟前
leetcode 2029. 石子游戏 IX 中等
leetcode
刘广睿1 小时前
素材去重为什么难?文件指纹、感知哈希与相似度判定的设计笔记
算法·哈希算法·产品设计·素材管理
少司府1 小时前
C++进阶:智能指针
开发语言·数据结构·c++·b树·算法·c·智能指针
鹿角片ljp2 小时前
LeetCode 22:括号生成|回溯dfs、剪枝与 char[] 覆盖代替撤销
算法·深度优先
sylviiiiiia2 小时前
leetcode hot100
python·算法·leetcode
纪伊路上盛名在2 小时前
Kabsch算法的Julia实现
开发语言·算法·julia·序列分析·蛋白质·rmsd
裕晟资质规划2 小时前
军工保密资质二级申报的四个可量化硬条件:条文位置、数值口径与西安配套企业实务要点
java·服务器·网络·数据库·算法
LuminousCPP2 小时前
数据结构-排序(一):基础排序算法横向对比|冒泡、插入、希尔、堆与双向选择,详解二分 / Knuth 增量
c语言·数据结构·笔记·算法·排序算法