27. 移除元素
给你一个数组 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
思路:
本题是经典的双指针算法题,对于一个数组来说,删除一个元素是比较复杂的,每次删除元素后,若要保留原有的顺序,则需要将后面的元素一个个先前“挪”一格位置,这样操作的话,算法的时间复杂度是较高的。
所以就有了双指针法,分为快慢指针法以及左右指针法,能够实现在一个for循环下完成两个for循环的工作。
两遍循环:
我们可以利用一种最朴素的方法,即暴力地使用两遍for循环进行求解,外层的for循环用于查找所要移除的元素,当寻找到所要移除的元素值后,利用内层的for循环,将数组集体向前移动一位。
//暴力解法
int removeElement(vector<int>& nums, int val) {
int size = nums.size();
for (int i = 0; i < size; i++) {
if (nums[i] == val) { // 发现需要移除的元素
for (int j = i + 1; j < size; j++) { //将数组集体向前移动一位
nums[j - 1] = nums[j];
}
i--; // 因为下标i以后的数值都向前移动了一位,所以i也向前移动一位
size--; // 记录此时的数组大小
}
}
return size;
}
快慢双指针:
设置快慢双指针,分别指向数组,完成各自任务。
快指针:不停止,勇往直前,寻找目标元素
慢指针:用于记录更新新数组的下标位置
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int slow = 0; //初始化慢指针
for (int fast = 0; fast < nums.size(); fast++) { //快指针对整个数组进行遍历,寻找目标值
if (val != nums[fast]) { //若不为目标值,更新慢指针,并赋值给新数组
nums[slow] = nums[fast];
slow++;
}
}
return slow;
}
};
总的来说快慢指针的概念还是挺容易理解的,使用起来也十分简介方便。
左右双指针:
左右双指针的思想与之前所提到的二分查找的思想比较相像,通过在数组的两端设置左指针以及右指针,让两个指针在中间汇合。
在循环过程中,当左指针找到等于val的元素时,左指针会停下来。当右指针找到不等于val的元素时,也会停下来,这时候,右指针所指向的不等于val的元素会赋值给左指针所指向的元素。接着,只要左右指针仍未汇合,他们就会继续朝着中间行走,重复刚刚的操作,直至两者相遇,结束对整个数组的遍历。
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int left = 0; //初始化左指针
int right = nums.size() - 1; //初始化右指针
while (left <= right) {
// 找左边等于val的元素
while (left <= right && nums[left] != val) { //若不等于目标值,继续遍历
++left;
} //找到等于val值的元素时,结束循环
// 找右边不等于val的元素
while (left <= right && nums[right] == val) {
--right;
} //找到不等于val值的元素时,结束循环
// 将右边不等于val的元素覆盖左边等于val的元素
if (left < right) {
nums[left++] = nums[right--];
}
}
return left; // left指向最终数组末尾的下一个元素
}
}
题解参考:“代码随想录”https://www.bilibili.com/video/BV12A4y1Z7LP
后续也会坚持更新我的LeetCode刷题笔记,欢迎大家关注我,一起学习。
如果这篇文章对你有帮助,或者你喜欢这篇题解,可以给我点个赞哦。
往期回顾:
LeetCode367.有效的完全平方数
LeetCode69.x的平方根
LeetCode35.搜索插入位置
LeetCode704.二分查找
LeetCode34.在排序数组中寻找元素的第一个和最后一个位置