力扣解题-27. 移除元素

5 阅读4分钟

力扣解题-27. 移除元素

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

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

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

用户评测: 评测机将使用以下代码测试您的解决方案:

int[] nums = [...]; // 输入数组
int val = ...; // 要移除的值
int[] expectedNums = [...]; // 长度正确的预期答案。
                            // 它以不等于 val 的值排序。

int k = removeElement(nums, val); // 调用你的实现

assert k == expectedNums.length;
sort(nums, 0, k); // 排序 nums 的前 k 个元素
for (int i = 0; i < actualLength; i++) {
    assert nums[i] == expectedNums[i];
}

如果所有的断言都通过,你的解决方案将会 通过。

示例 1: 输入:nums = [3,2,2,3], val = 3 输出:2, nums = [2,2,,] 解释:你的函数应该返回 k = 2, 并且 nums 中的前两个元素均为 2。 你在返回的 k 个元素之外留下了什么并不重要(因此它们并不计入评测)。

示例 2: 输入:nums = [0,1,2,2,3,0,4,2], val = 2 输出:5, nums = [0,1,4,0,3,,,_] 解释:你的函数应该返回 k = 5,并且 nums 中的前五个元素为 0,0,1,3,4。 注意这五个元素可以任意顺序返回。 你在返回的 k 个元素之外留下了什么并不重要(因此它们并不计入评测)。

提示:

0 <= nums.length <= 100

0 <= nums[i] <= 50

0 <= val <= 100


第一次解答

解题思路

核心方法:快慢双指针(读写指针)原地覆盖,通过一个“读指针”遍历数组,一个“写指针”记录非目标值的写入位置,仅一次遍历完成原地移除,时间复杂度O(n)、空间复杂度O(1),是本题的最优解法。

核心原理铺垫

题目要求“原地移除”,且仅关注前k个非val元素(后续元素无要求),因此无需创建新数组,只需将非val元素依次覆盖到数组前半部分:

  • 读指针(read):负责遍历整个数组,检查每个元素是否为val;
  • 写指针(write):负责记录“下一个非val元素应该写入的位置”,初始为0(从数组开头开始写入)。
具体步骤
  1. 初始化写指针int write = 0,表示第一个非val元素将写入nums[0]的位置;
  2. 读指针遍历数组for (int read = 0; read < nums.length; read++),逐个检查数组元素:
    • 若当前元素nums[read] != val(非目标值,需要保留):
      • 将该元素写入写指针位置:nums[write] = nums[read]
      • 写指针右移一位(write++),为下一个非val元素预留位置;
    • 若当前元素nums[read] == val(目标值,需要移除):不做任何操作,读指针继续前进,写指针保持不动;
  3. 返回结果:遍历完成后,write的值即为数组中非val元素的数量k(因为每写入一个非val元素,write就加1,最终write的数值等于有效元素的个数)。
核心优化逻辑说明
  1. 时间复杂度最优:仅需一次遍历数组(O(n)),每个元素仅被读指针访问一次,无嵌套循环或冗余计算,因此耗时0ms击败100%用户;
  2. 空间复杂度极致:全程仅使用两个指针变量(write、read),无额外数组/集合创建,完全满足“原地操作”的要求;
  3. 逻辑适配题目评测规则
    • 无需关心前k个元素的顺序(题目允许元素顺序改变),也无需处理k之后的元素(评测机仅校验前k个元素);
    • 写指针的移动逻辑天然保证前k个位置存储的是所有非val元素,完美契合题目对数组修改的要求;
  4. 内存表现说明:内存消耗43MB仅击败22.89%用户,并非逻辑问题,而是评测机环境差异(如JVM内存分配、其他进程占用等),该解法的内存开销已达到理论最优(O(1))。

执行耗时:0 ms,击败了100.00% 的Java用户 内存消耗:43 MB,击败了22.89% 的Java用户

    public int removeElement(int[] nums, int val) {
        int write=0;
        for (int read = 0; read < nums.length; read++) {
            if(nums[read]!=val){
                nums[write]=nums[read];
                write++;
            }
        }
        return write;

    }

总结

  1. 该解法的核心是快慢双指针的分工协作:读指针负责“扫描”,写指针负责“留存有效元素”,仅一次遍历完成原地修改,是数组原地操作类问题的经典技巧;
  2. 无需额外空间、无需复杂逻辑,仅通过简单的条件判断和指针移动,就满足了题目“原地移除”和“返回有效元素数量”的核心要求;
  3. 时间复杂度O(n)、空间复杂度O(1),是本题的理论最优解,完全适配题目数据规模(nums.length ≤ 100)。