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:快慢指针通用记忆口诀

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

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

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