1、题目
2、错误分析
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
//暴力解法,两层for循环
int size=nums.size();
for(int i=0;i<size;i++){
if(nums[i]==val){
for(int j=i;j<size-1;j++){
nums[j]=nums[j+1];//注意,这里不要超出数组索引范围
}
--i;//因为i++,所以还要判断i这个位置就需要--i;
--size;
}
}
return size;
}
};
(1)暴力解法,两个for循环,第一个for循环找到之后,第二个for循环将后面元素依次前移,时间复杂度是n2;时间复杂度:O(n^2);空间复杂度:O(1);
3、题解
采用快慢指针,示意图如下,来自代码随想录
(1)方法一:快慢指针,不会改变其相对位置(顺序),搬运元素次数=所有元素数-目标元素数;最后slow指向结尾元素;两个指针之差就是替换的目标元素数量;
(2)low指针指向当前要处理的元素,fast指针指向下一个将要赋值的元素,整个过程中保持不变的是,区间[0,low)的元素都不等于val,因此low就是数组长度;
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
//采用快慢指针
//int size=nums.size();不用size这个变量了
int low=0;
for(int fast=0;fast<nums.size();fast++){
if(nums[fast]!=val){//在不相等的时候依次覆盖
nums[low++]=nums[fast];
}
}
return low;//注意,这里是Low,不是low+1
}
};
// 时间复杂度:O(n),其中n为序列长度,最多遍历序列至多两次
// 空间复杂度:O(1),我们只需要常数空间保持若干变量
(3)方法二:双向指针,左右两个指针往中间走,左指针查找是value的位置,右指针指向不是value的位置,然后将右指针指向的元素搬到左指针,同时两指针往中间走,会改变数组的顺序(相对位置),但是可以搬运最少的数组元素;注意,最后搜寻结束时,左指针一定指向结尾的下一个元素位置;
顺着上一个区间的思考方式,[0,left)一直是正确的区间,保证此区间没有val值;right找到的val值相当于直接丢弃,需要保留的会赋给left指向的空间;一直到左右指针交汇;
方法二:避免了需要保留了元素的重复赋值操作;
【双向指针并未完全理解掌握,粘贴代码如下】,这个<=和<有点懵
/**
* 相向双指针方法,基于元素顺序可以改变的题目描述改变了元素相对位置,确保了移动最少元素
* 时间复杂度:$O(n)$
* 空间复杂度:$O(1)$
*/
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int leftIndex = 0;
int rightIndex = nums.size() - 1;
while (leftIndex <= rightIndex) {
// 找左边等于val的元素
while (leftIndex <= rightIndex && nums[leftIndex] != val){
++leftIndex;
}
// 找右边不等于val的元素
while (leftIndex <= rightIndex && nums[rightIndex] == val) {
-- rightIndex;
}
// 将右边不等于val的元素覆盖左边等于val的元素
if (leftIndex < rightIndex) {
nums[leftIndex++] = nums[rightIndex--];
}
}
return leftIndex; // leftIndex一定指向了最终数组末尾的下一个元素
}
};