移除元素算法

移除元素

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

  • 原地修改:不能使用额外的数组空间。
  • 元素顺序可以改变:对数组中剩余元素的顺序没有要求。
  • 返回值:新的数组长度 k,其中前 k 个元素为不等于 val 的元素。
示例

示例 1:

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2,_,_]
解释:函数返回的 k = 2,nums 的前两个元素为 2。

示例 2:

输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,3,0,4,_,_,_]
解释:函数返回的 k = 5,nums 的前五个元素可以是 0、1、3、0、4,顺序不重要。
解题思路

由于需要原地移除元素,并且元素的顺序可以改变,我们可以使用双指针方法来解决这个问题。

  • 指针 k:表示当前不等于 val 的元素的插入位置,也是最终返回的新数组长度。
  • 遍历指针 i:用于遍历整个数组。

算法步骤:

  1. 初始化一个变量 k = 0,表示当前不等于 val 的元素个数。
  2. 遍历数组 nums
    • 如果 nums[i] 不等于 val,则将 nums[i] 赋值给 nums[k],并递增 k
    • 如果 nums[i] 等于 val,则跳过,不做任何操作。
  3. 遍历结束后,nums 的前 k 个元素即为不等于 val 的元素。
代码详解
function removeElement(nums: number[], val: number): number {
    let k = 0; // 用于记录不等于 val 的元素个数
    for (let i = 0; i < nums.length; i++) {
        if (nums[i] !== val) {
            nums[k] = nums[i]; // 将当前元素移到前面第 k 个位置
            k++; // 增加计数器
        }
        // 如果 nums[i] === val,什么都不做,直接跳过
    }
    return k; // 返回不等于 val 的元素个数
}

解释:

  • 变量初始化:
    • let k = 0;:初始化计数器 k,表示不等于 val 的元素数量。
  • 遍历数组:
    • for (let i = 0; i < nums.length; i++) { ... }:遍历数组的每个元素。
  • 条件判断:
    • if (nums[i] !== val) { ... }:检查当前元素是否不等于 val
  • 元素移动:
    • nums[k] = nums[i];:将不等于 val 的元素移动到前面位置 k
    • k++;:更新计数器,准备存放下一个不等于 val 的元素。
  • 返回结果:
    • return k;:返回新的数组长度,即不等于 val 的元素个数。
示例演示

以示例 2 为例,nums = [0,1,2,2,3,0,4,2], val = 2

  • 初始状态:

    • nums = [0,1,2,2,3,0,4,2]
    • k = 0
  • 遍历过程:

    1. i = 0
      • nums[0] = 00 !== 2,所以:
      • nums[k] = nums[0];nums[0] 赋值给 nums[0],无变化)
      • k = 1
    2. i = 1
      • nums[1] = 11 !== 2,所以:
      • nums[k] = nums[1];nums[1] 赋值给 nums[1],无变化)
      • k = 2
    3. i = 2
      • nums[2] = 22 === 2,跳过
    4. i = 3
      • nums[3] = 22 === 2,跳过
    5. i = 4
      • nums[4] = 33 !== 2,所以:
      • nums[k] = nums[4];nums[2] 赋值为 3
      • nums = [0,1,3,2,3,0,4,2]
      • k = 3
    6. i = 5
      • nums[5] = 00 !== 2,所以:
      • nums[k] = nums[5];nums[3] 赋值为 0
      • nums = [0,1,3,0,3,0,4,2]
      • k = 4
    7. i = 6
      • nums[6] = 44 !== 2,所以:
      • nums[k] = nums[6];nums[4] 赋值为 4
      • nums = [0,1,3,0,4,0,4,2]
      • k = 5
    8. i = 7
      • nums[7] = 22 === 2,跳过
  • 结果:

    • 返回 k = 5
    • nums 的前 5 个元素为 [0,1,3,0,4]
时间和空间复杂度
  • 时间复杂度:O(n),其中 n 是数组 nums 的长度。需要遍历一次数组。
  • 空间复杂度:O(1),只使用了常数级别的额外空间。
总结
  • 双指针法:利用一个快指针遍历数组,一个慢指针记录新数组的位置。
  • 原地修改:不需要额外的数组空间,直接在 nums 上进行修改。
  • 顺序无关:题目允许改变元素的顺序,但本解法保持了原有的元素顺序。
注意事项
  • 题目要求:返回的不等于 val 的元素可以是任意顺序,但在本解法中,我们保持了元素的相对顺序。
  • 测试用例:在评测时,可能需要对 nums 的前 k 个元素进行排序以通过断言检查,但根据题目描述,顺序并不重要。
扩展

如果题目要求可以改变元素的顺序,且希望进一步优化,可以使用首尾双指针的方法,在某些情况下可以减少操作次数。

示例代码:

function removeElement(nums: number[], val: number): number {
    let left = 0;
    let right = nums.length - 1;
    while (left <= right) {
        if (nums[left] === val) {
            nums[left] = nums[right];
            right--;
        } else {
            left++;
        }
    }
    return right + 1;
}

注意:这种方法会改变元素的顺序,但可能在某些情况下更高效。


加油兄弟们!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小李学软件

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值