双指针题目:移除元素

文章目录

题目

标题和出处

标题:移除元素

出处:27. 移除元素

难度

3 级

题目描述

要求

给定一个整数数组 nums \texttt{nums} nums 和一个整数 val \texttt{val} val,原地删除 nums \texttt{nums} nums 中的所有 val \texttt{val} val。元素的相对顺序可以改变。

由于在某些语言中不能改变数组的长度,所以必须将结果放在数组 nums \texttt{nums} nums 的靠前部分 。更规范地说,如果在删除 val \texttt{val} val 之后有 k \texttt{k} k 个元素,那么 nums \texttt{nums} nums 的前 k \texttt{k} k 个元素应该保存最终结果。不需要考虑数组中超出 k \texttt{k} k 部分的元素。

将最终结果插入 nums \texttt{nums} nums 的前 k \texttt{k} k 个位置后返回 k \texttt{k} k。

要求不能创建新数组。必须原地修改输入数组 ,使用 O(1) \texttt{O(1)} O(1) 额外空间。

判题标准

系统会用下面的代码来测试解法:

复制代码
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 < k; i++) {
    assert nums[i] == expectedNums[i];
}

如果所有断言都通过,那么解法通过

示例

示例 1:

输入: nums = [3,2,2,3], val = 3 \texttt{nums = [3,2,2,3], val = 3} nums = [3,2,2,3], val = 3

输出: 2, nums = [2,2,,] \texttt{2, nums = [2,2,\,\]} 2, nums = [2,2,,]

解释:函数应该返回 k = 2 \texttt{k = 2} k = 2,并且原数组 nums \texttt{nums} nums 的前两个元素为 2 \texttt{2} 2。不需要考虑数组中超出返回值 k \texttt{k} k 部分的元素(因此用下划线表示)。

示例 2:

输入: nums = [0,1,2,2,3,0,4,2], val = 2 \texttt{nums = [0,1,2,2,3,0,4,2], val = 2} nums = [0,1,2,2,3,0,4,2], val = 2

输出: 5, nums = [0,1,4,0,3,,,] \texttt{5, nums = [0,1,4,0,3,\,\,\]} 5, nums = [0,1,4,0,3,,,_]

解释:函数应该返回 k = 5 \texttt{k = 5} k = 5,并且原数组 nums \texttt{nums} nums 的前五个元素为 0 \texttt{0} 0、 0 \texttt{0} 0、 1 \texttt{1} 1、 3 \texttt{3} 3 和 4 \texttt{4} 4。注意这五个元素可以是任意顺序。不需要考虑数组中超出返回值 k \texttt{k} k 部分的元素(因此用下划线表示)。

数据范围

  • 0 ≤ nums.length ≤ 100 \texttt{0} \le \texttt{nums.length} \le \texttt{100} 0≤nums.length≤100
  • 0 ≤ nums[i] ≤ 50 \texttt{0} \le \texttt{nums[i]} \le \texttt{50} 0≤nums[i]≤50
  • 0 ≤ val ≤ 100 \texttt{0} \le \texttt{val} \le \texttt{100} 0≤val≤100

解法一

思路和算法

这道题要求删除数组 nums \textit{nums} nums 中的所有等于 val \textit{val} val 的元素,并将其余元素填入结果数组,可以使用双指针实现。

定义快指针 fast \textit{fast} fast 和慢指针 slow \textit{slow} slow,快指针用于遍历数组,慢指针用于将不等于 val \textit{val} val 的元素填入结果数组,初始时两个指针都指向下标 0 0 0。快指针遍历数组的过程中,需要判断当前元素是否等于 val \textit{val} val,如果不等于 val \textit{val} val 则填入结果数组,否则跳过当前元素,具体做法如下。

  • 如果 nums [ fast ] ≠ val \textit{nums}[\textit{fast}] \ne \textit{val} nums[fast]=val,则不删除当前元素,因此将 nums [ fast ] \textit{nums}[\textit{fast}] nums[fast] 填到 nums [ slow ] \textit{nums}[\textit{slow}] nums[slow],然后将 slow \textit{slow} slow 向右移动一位。

  • 如果 nums [ fast ] = val \textit{nums}[\textit{fast}] = \textit{val} nums[fast]=val,则删除当前元素,因此跳过当前元素。

由于快指针的移动次数一定大于等于慢指针的移动次数,因此将 nums [ fast ] \textit{nums}[\textit{fast}] nums[fast] 填到 nums [ slow ] \textit{nums}[\textit{slow}] nums[slow] 不会覆盖尚未遍历的元素。

遍历结束之后,下标范围 [ 0 , slow − 1 ] [0, \textit{slow} - 1] [0,slow−1] 的元素为数组中删除所有等于 val \textit{val} val 的元素后的所有元素,结果数组有 slow \textit{slow} slow 个元素,返回 slow \textit{slow} slow。

代码

java 复制代码
class Solution {
    public int removeElement(int[] nums, int val) {
        int length = nums.length;
        int fast = 0, slow = 0;
        while (fast < length) {
            if (nums[fast] != val) {
                nums[slow] = nums[fast];
                slow++;
            }
            fast++;
        }
        return slow;
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 nums \textit{nums} nums 的长度。双指针各遍历数组一次。

  • 空间复杂度: O ( 1 ) O(1) O(1)。

解法二

思路和算法

解法一可以使用快慢指针从左到右遍历数组,结果数组中的元素与原数组中的元素的相对顺序相同。由于结果数组中的元素顺序可以改变,因此也可以使用反向双指针,其主要思想是将结果数组中的等于 val \textit{val} val 的元素与结果数组外的不等于 val \textit{val} val 的元素交换。

用 left \textit{left} left 和 right \textit{right} right 表示两个指针, left \textit{left} left 用于将不等于 val \textit{val} val 的元素填入结果数组, right \textit{right} right 用于将等于 val \textit{val} val 的元素填到结果数组外的部分,初始时 left \textit{left} left 和 right \textit{right} right 分别指向数组的最小下标和最大下标。

每次检查 nums [ left ] \textit{nums}[\textit{left}] nums[left] 是否等于 val \textit{val} val,判断是否需要将 nums [ left ] \textit{nums}[\textit{left}] nums[left] 交换到结果数组外,具体做法如下。

  • 如果 nums [ left ] = val \textit{nums}[\textit{left}] = \textit{val} nums[left]=val,则需要将 nums [ left ] \textit{nums}[\textit{left}] nums[left] 交换到结果数组外,因此将 nums [ left ] \textit{nums}[\textit{left}] nums[left] 和 nums [ right ] \textit{nums}[\textit{right}] nums[right] 交换,然后将 right \textit{right} right 向左移动一位。由于在交换之后, nums [ left ] \textit{nums}[\textit{left}] nums[left] 仍可能等于 val \textit{val} val,因此不能将 left \textit{left} left 向右移动。

  • 如果 nums [ left ] ≠ val \textit{nums}[\textit{left}] \ne \textit{val} nums[left]=val,则不需要将 nums [ left ] \textit{nums}[\textit{left}] nums[left] 交换到结果数组外,因此将 left \textit{left} left 向右移动一位。

每次操作之后,可以确保下标 left \textit{left} left 左侧的元素都不等于 val \textit{val} val,下标 right \textit{right} right 右侧的元素都等于 val \textit{val} val。

当 left ≤ right \textit{left} \le \textit{right} left≤right 时,存在尚未处理的元素,因此重复上述操作。当 left > right \textit{left} > \textit{right} left>right 时,所有元素都被处理,因此结束操作,此时下标范围 [ 0 , left − 1 ] [0, \textit{left} - 1] [0,left−1] 的元素为数组中删除所有等于 val \textit{val} val 的元素后的所有元素,结果数组有 left \textit{left} left 个元素,返回 left \textit{left} left。

代码

java 复制代码
class Solution {
    public int removeElement(int[] nums, int val) {
        int length = nums.length;
        int left = 0, right = length - 1;
        while (left <= right) {
            if (nums[left] == val) {
                int temp = nums[left];
                nums[left] = nums[right];
                nums[right] = temp;
                right--;
            } else {
                left++;
            }
        }
        return left;
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 nums \textit{nums} nums 的长度。双指针遍历数组一次。

  • 空间复杂度: O ( 1 ) O(1) O(1)。

相关推荐
程序员-King.4 天前
双指针/滑动窗口—算法总结与教学指南
经验分享·算法·双指针
程序员-King.4 天前
day115—同向双指针—将x减到0的最小操作数(LeetCode-1658)
算法·leetcode·双指针
程序员-King.4 天前
day114—同向双指针(数组)—统计得分小于K的子数组数目(LeetCode-2302)
算法·leetcode·双指针
程序员-King.5 天前
day109—同向双指针(字符串)—每个字符最多出现两次的最长子字符串(LeetCode-3090)
算法·leetcode·双指针
程序员-King.5 天前
day110—同向双指针(数组)—最多K个重复元素的最长子数组(LeetCode-2958)
算法·leetcode·双指针
程序员-King.5 天前
day107—同向双指针—无重复字符的最长字串(LeetCode-3)
算法·leetcode·双指针
程序员-King.5 天前
day108—同向双指针—乘积小于K的子数组(LeetCode-713)
算法·leetcode·双指针
不穿格子的程序员11 天前
从零开始刷算法——双指针-三数之和&接雨水
算法·双指针
hansang_IR20 天前
【记录】四道双指针
c++·算法·贪心·双指针