在问 · 算法题解

27. 移除元素

简单双指针快慢双指针力扣原题 ↗

题目描述

输入整数数组 nums 和一个值 val。设 nums 中不等于 val 的元素共有 k 个。

直接在 nums 上修改,不要开新数组:把这 k 个元素放到 nums 的前 k 个位置,它们之间的顺序可以改变。后面的位置放什么都不影响判题。

函数返回 k。判题时会先核对 k,再把 nums 前 k 个元素排序后与标准答案比较。

本页示例输出写成 [k, nums 的前 k 个元素] 的形式。

示例 1
输入:nums = [3,2,2,3], val = 3
输出:[2,[2,2]]

解释:去掉两个 3 后剩下两个 2,返回 2。

示例 2
输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:[5,[0,1,3,0,4]]

解释:去掉三个 2 后剩下 0,1,3,0,4 五个数,返回 5;这五个数的顺序不影响判题。

数据范围

  • 0 <= nums.length <= 100
  • 0 <= nums[i] <= 50
  • 0 <= val <= 100

解题思路:快慢双指针

用快慢指针。快指针从左到右读每个数 x;慢指针 k 是下一个要写入的位置。x 不等于 val 就把它写到 nums[k],k 加 1;x 等于 val 就跳过。因为 k 不会超过读取位置,写入只会覆盖已经读过的数。

为什么正确

nums[:k] 恰好是已读部分中所有不等于 val 的数,并保持原顺序。

复杂度分析

时间 O(n),每个数读一次、最多写一次;空间 O(1)。

易错点

不要在遍历时用 pop/remove 删除元素:下标会错位,而且每次删除是 O(n),整体退化为 O(n²)。返回的是个数 k,不是数组。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        k = 0
        for x in nums:
            if x != val:
                nums[k] = x
                k += 1
        return k

载入编辑器,自己跑一遍 →

其他解法

两种方法都是 O(n) 时间、O(1) 空间;快慢指针保持原顺序,首尾指针不保序但在待删元素少时赋值更少。

解法 2:首尾双指针 时间 O(n),空间 O(1)

left 从头、right 从尾部之后出发,[left, right) 是待检查区间。nums[left] 等于 val 时,用区间最后一个元素 nums[right-1] 覆盖它,并让 right 减 1,left 不动,因为换来的元素还没检查。否则 left 加 1。两指针相遇时 left 就是保留元素的个数。

每个元素最多被访问一次,要删的元素很少时赋值次数远少于快慢指针。代价是会打乱保留元素的相对顺序,本题允许这样做。

class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        left, right = 0, len(nums)
        while left < right:
            if nums[left] == val:
                nums[left] = nums[right - 1]
                right -= 1
            else:
                left += 1
        return left

双指针 专题的其他题目

查看 双指针 全部题目 →