27. 移除元素(C++)
题目
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
示例 1:
给定 nums = [3,2,2,3], val = 3,
函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。
你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,1,2,2,3,0,4,2], val = 2,
函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。
注意这五个元素可为任意顺序。
你不需要考虑数组中超出新长度后面的元素。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参作任何拷贝
int len = removeElement(nums, val);
// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}
分析与题解
循环嵌套暴力解
思路很清晰,我们先循环遍历一个数组,当查询到目标值元素后,再开一个循环把该下标后面的所有元素向前移位依次赋值。
因为题目说明不需要考虑返回长度外的数组元素,所以可以略过
完整代码如下:
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int n = nums.size();
for (int i = 0; i < n; i++) {
if (nums[i] == val) {
for (int j = i + 1; j < n; j++) {
nums[j - 1] = nums[j];
}
// 可以立即改变外层循环的边界值
n--;
// 因为目标元素后的所有元素全部向前移位
// 所以当前下标的元素进行了更新,需要在该下标再次进行讨论
i--;
}
}
return n;
}
};
暴力解(优化)
题目对于返回数组的元素顺序没有要求。我们尝试查询所有相同元素后,批量地对身后的元素进行移动。
首先对数组进行排序,保证存在的目标元素相邻进行排列:
sort(nums.begin(), nums.end());
我们设置两个变量:
int last = -1;
int num = 0;
其中last记录末位目标元素的下标位置,num记录数组中目标元素的个数。那么之后批量对目标值的位置进行填充时,首位填充元素的下标即为last - num + 1
。另外需要注意的是,能填充的元素个数与目标值个数num无关,而是由数组的边界进行限制
for (int i = last - num + 1; i + num < n; i++) {
}
算法复杂度从O(n^2)优化至O(n),完整代码如下:
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
// 先对数组进行排序
sort(nums.begin(), nums.end());
int n = nums.size();
// last记录末位目标元素的下标
// (这样会好处理些))
// num记录数组中目标元素的个数
int last = -1;
int num = 0;
for (int i = 0; i < n; i++) {
if (nums[i] == val) {
last = i;
num++;
}
}
for (int i = last - num + 1; i + num < n; i++) {
nums[i] = nums[i + num];
}
return n - num;
}
};
双指针法
双指针法又被称为快慢指针法,在数组和链表的操作中十分常见。其中快指针用于遍历整个数组,而慢指针只会记录非目标值的情况:
- 在出现目标值之前,慢指针跟快指针会同步更新
- 出现目标值之后,慢指针跟快指针出现交错。慢指针会卡在目标值所在的下标处,当快指针再次指向非目标值元素后,就会把该值覆盖掉目标值元素。
完整代码如下:
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int n = nums.size();
int slowIndex = 0;
for (int fastIndex = 0; fastIndex < n; fastIndex++) {
if (nums[fastIndex] != val) {
nums[slowIndex] = nums[fastIndex];
slowIndex++;
}
}
return slowIndex;
}
};