文章目录
题目
标题和出处
标题:移除元素
出处: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)。