【代码随想录刷题总结】leetcode27-移除元素

引言

大家好啊,我是前端拿破轮😁。

跟着卡哥学算法有一段时间了,通过代码随想录的学习,受益匪浅,首先向卡哥致敬🫡。

但是在学习过程中我也发现了一些问题,很多当时理解了并且AC的题目过一段时间就又忘记了,或者不能完美的写出来。根据费曼学习法 ,光有输入的知识掌握的是不够牢靠的,所以我决定按照代码随想录的顺序,输出自己的刷题总结和思考 。同时,由于以前学习过程使用的是JavaScript,而在2025年的今天,TypeScript几乎成了必备项,所以本专题内容也将使用TypeScript,来巩固自己的TypeScript语言能力。

题目信息

移除元素

leetcode题目链接

给你一个数组 nums 和一个值 val,你需要原地 移除所有数值等于 val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。

假设 nums 中不等于 val 的元素数量为 k,要通过此题,您需要执行以下操作:

更改 nums 数组,使 nums 的前 k 个元素包含不等于 val 的元素。nums的其余元素和 nums 的大小并不重要。 返回 k

题目分析

这个题乍一看不是很难,可能很多同学想到直接用一个filter不就解决了吗?但是仔细看,才会发现这道题的玄机,它要求我们原地 移除值等于val的元素,也就是说空间复杂度必须是O(1).

那有的同学可能又要想了,filter方法不修改原数组,而是返回新的数组,确实不符合题意,那我直接使用splice方法不就可以在原数组上进行操作了吗?于是高兴地写出了如下代码:

ts 复制代码
// 以下是错误代码
function removeElement(nums: number[], val: number): number {
  // 遍历数组,如果当前元素等于val,则用splice移除
  for (let i = 0; i < nums.length; i++) {
    if (nums[i] === val) {
      nums.splice(i, 1);
    }
  }
  return nums.length;
};

然后就会发现,代码报错了。

我们对比输出和预期结果可以发现,在原数组中有连续两个2时,我们的代码只删除了其中的一个。

这是为什么呢?其实是因为我们在遍历nums的过程中又对nums本身的元素进行了删除,使得我们会跳过部分元素。

当程序运行到i = 2的时候,此时发现符合我们的分支判断,于是用splice方法删除了当前的2,那么数组中后面剩余的元素都要进行左移一位(因为数组在内存中是连续存放的)。

那么第二个2就被移动到了当前位置,但是此次循环已经结束,我们的i自动+1,就移动到了下一个位置,跳过了对于第二个2的处理。

所以代码应该修改为while而不是if.

题解

API法

ts 复制代码
// 以下是修正后的代码
function removeElement(nums: number[], val: number): number {
  // 遍历数组,当前元素等于val时,则用splice移除
  for (let i = 0; i < nums.length; i++) {
    while (nums[i] === val) {
      nums.splice(i, 1);
    }
  }
  return nums.length;
};

修改为while之后,代码可以成功AC。但是这种使用自带API的解法并不是本题考查的本意。此外,这种方式的时间复杂度会达到 <math xmlns="http://www.w3.org/1998/Math/MathML"> O ( n 2 ) O(n^2) </math>O(n2)。

这是因为splice方法实际上是一个O(n)的时间复杂度,而不是O(1)。因为数组在内存中连续存取的性质,在删除元素后,需要将后面的元素都向前移动一位。

所以,本题最优的解法应该是使用双指针。

双指针法

问题的关键在于,题目中的描述nums的其余元素和nums的大小并不重要

也就是说,实际上并不需要真的对nums进行删除元素的操作,只需要将不等于val的元素都移动到nums中的前面位置即可。

双指针法的核心是快慢指针,其中快指针对nums进行遍历,不断右移。

而慢指针则是指向要更新元素的位置,如果当前快指针所指的元素不等于val,则将其赋值给慢指针所指的位置,并将慢指针右移一位,否则,不进行操作。

这样,当快指针遍历完一遍后,所有不等于val的元素已经到了nums的最前面。直接返回此时慢指针的位置就是nums的有效长度。

ts 复制代码
// 双指针解法
function removeElement(nums: number[], val: number): number {
  // 慢指针
  let slow = 0;

  // 快指针直接遍历
  for (let i = 0; i < nums.length; i++) {
    // 如果当前值不等于val,则赋值给慢指针位置,并将慢指针右移一位
    if (nums[i] !== val) {
      nums[slow] = nums[i];
      slow++;
    }
  }
  return slow;
}

复杂度分析:

时间复杂度: <math xmlns="http://www.w3.org/1998/Math/MathML"> O ( n ) O(n) </math>O(n) 空间复杂度: <math xmlns="http://www.w3.org/1998/Math/MathML"> O ( 1 ) O(1) </math>O(1)

只有快指针进行了一趟遍历,时间复杂度是 <math xmlns="http://www.w3.org/1998/Math/MathML"> O ( n ) O(n) </math>O(n)

只占用了常数级别的空间,即两个指针,空间复杂度是 <math xmlns="http://www.w3.org/1998/Math/MathML"> O ( 1 ) O(1) </math>O(1)

总结

本文讨论了leetcode27的移除元素题目,分别分析了API法和双指针法,针对原地删除做了深入的分析,给出了两种方法的TypeScript的AC代码,分析了对应的时间和空间复杂度。

好了,这篇文章就到这里啦,如果对您有所帮助,欢迎点赞,收藏,分享👍👍👍。您的认可是我更新的最大动力。

往期推荐✨✨✨

我是前端拿破轮,我们下期见!

相关推荐
炽烈小老头几秒前
【每天学习一点算法 2025/12/22】将有序数组转换为二叉搜索树
学习·算法
jghhh0113 分钟前
POCS(凸集投影)算法解决部分k空间数据缺失导致吉布斯伪影
算法
罗湖老棍子19 分钟前
最小函数值(minval)(信息学奥赛一本通- P1370)
数据结构·c++·算法··优先队列·
LYFlied20 分钟前
【每日算法】LeetCode 4. 寻找两个正序数组的中位数
算法·leetcode·面试·职场和发展
长安er20 分钟前
LeetCode 62/64/5/1143多维动态规划核心题型总结
算法·leetcode·mybatis·动态规划
鹿角片ljp21 分钟前
力扣 83: 删除排序链表中的重复元素(Java实现)
java·leetcode·链表
LYFlied26 分钟前
【每日算法】LeetCode 208. 实现 Trie (前缀树)
数据结构·算法·leetcode·面试·职场和发展
代码游侠1 小时前
应用——MPlayer 媒体播放器系统代码详解
linux·运维·笔记·学习·算法
学编程就要猛1 小时前
算法:3.快乐数
java·算法
AI科技星1 小时前
统一场论框架下万有引力常数的量子几何涌现与光速关联
数据结构·人工智能·算法·机器学习·重构