【代码随想录刷题总结】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代码,分析了对应的时间和空间复杂度。

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

往期推荐✨✨✨

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

相关推荐
Fanxt_Ja1 小时前
【JVM】三色标记法原理
java·开发语言·jvm·算法
luofeiju1 小时前
行列式的性质
线性代数·算法·矩阵
緈福的街口1 小时前
【leetcode】347. 前k个高频元素
算法·leetcode·职场和发展
pen-ai2 小时前
【统计方法】基础分类器: logistic, knn, svm, lda
算法·机器学习·支持向量机
鑫鑫向栄2 小时前
[蓝桥杯]春晚魔术【算法赛】
算法·职场和发展·蓝桥杯
roman_日积跬步-终至千里2 小时前
【Go语言基础【3】】变量、常量、值类型与引用类型
开发语言·算法·golang
FrankHuang8883 小时前
使用高斯朴素贝叶斯算法对鸢尾花数据集进行分类
算法·机器学习·ai·分类
菠萝013 小时前
共识算法Raft系列(1)——什么是Raft?
c++·后端·算法·区块链·共识算法
武子康3 小时前
AI炼丹日志-28 - Audiblez 将你的电子书epub转换为音频mp3 做有声书
人工智能·爬虫·gpt·算法·机器学习·ai·音视频
闪电麦坤954 小时前
数据结构:泰勒展开式:霍纳法则(Horner‘s Rule)
数据结构·算法