题目:两数之和
给你一个数组nums
和一个值val
,你需要原地移除所有数值等于val
的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
- 示例 1:
输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。
- 示例 2:
输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。
- 提示:
0 <= nums.length <= 100
0 <= nums[i] <= 50
0 <= val <= 100
思路1
我们可以保留两个指针i
和j
,其中i
是慢指针,j
是快指针。当nums[j]
与给定的值相等时,递增j
以跳过该元素。只要nums[j] ≠ val
,我们就把nums[j]
赋值到nums[i]
并同时递增两个索引。重复这一过程,直到j
到达数组的末尾,该数组的新长度为i
。
- 时间复杂度:O(n)
- 空间复杂度:O(1)
代码
public int removeElement(int[] nums, int val) { // 总时间复杂度O(n)
if (nums.length == 0) {
return 0;
}
// 慢指针
int i = 0;
// 快指针
int j = 0;
while (j <= nums.length - 1) { // 时间复杂度O(n)
if (nums[j] != val) {
nums[i] = nums[j];
i++;
}
j++;
}
return i;
}
思路2
考虑数组包含很少的要删除的元素的情况。例如,nums = {1, 2, 3, 5, 4},val = 4
,之前的算法会对前四个元素做不必要的赋值操作。另一个例子是nums = {4, 1, 2, 3, 5},val = 4
,似乎没有必要将{1,2,3,5}
这几个元素左移一步,因为问题描述中提到元素的顺序可以更改。
可以考虑遇到nums[i] = val
时,将当前元素与最后一个元素进行交换,并释放最后一个元素。这实际上使数组的大小减少了1。
- 时间复杂度:O(n)
- 空间复杂度:O(1)
代码
public int removeElement(int[] nums, int val) { // 总时间复杂度O(n)
if (nums.length == 0) {
return 0;
}
int i = 0;
int length = nums.length;
while (i < length) { // 时间复杂度O(n)
// nums[left]等于目标值的时候,nums[left]位置存放最后一个元素,把最后一个元素丢弃掉
if (nums[i] == val) {
nums[i] = nums[length - 1];
length--;
} else {
i++;
}
}
return length;
}