给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
暴力解法不过多介绍,就是循环就行了。但是注意细节要时刻记录数组的长度毕竟最后要返回数组长度的,还有点就是我们在定义函数的时候,参数列表里如果有数组或者其他参数的话,加和不加&是由区别的,加了即代表引用,那么数组发生变化可以直接被看见,也就是说在函数里是可以修改数组的。
主要介绍的是另一种写法即双指针写法,后面还会经常用到。原来的循环要所有都遍历完,那么双指针就可以通过两个指针向中间遍历,从而减少了时间复杂度,指针如何定义呢?
分别占据左右端点,然后对于左边的指针它是用来寻找等于val的元素的,右边的指针是用来寻找右边不等于Val元素的,
两个循环如下:
while (slowIndex <= fastIndex && nums[slowIndex] != val)
{
slowIndex ++;//可以看看这里先加和后加的区别
}
//找右边不等于val的元素
while (slowIndex <= fastIndex && nums[fastIndex] ==val)
{
fastIndex --;//同理
}//这里上述两个循环是为了寻找元素接下来是要进行元素覆盖
注意循环里的条件,由于涉及到加减操作,所以判读里肯定要包含,然后就是左边找等于val,所以我们判断不等于val的时候才需要指针向前移动,
然后就是替代,
if (slowIndex < fastIndex)
{
nums[slowIndex++] = nums[fastIndex--];
}
注意这里面的加加减减,因为每次置换之后都要继续往下移动,否则两个指针还是留在原地的。
这些都是在一个大循环while里的,完整代码:
class Solution
{
public:
int removeElement(vector<int> &nums, int val)
{
int slowIndex = 0;
int fastIndex = nums.size()-1;
while (slowIndex <= fastIndex)
{
//找左边等于val的元素
while (slowIndex <= fastIndex && nums[slowIndex] != val)
{
slowIndex ++;//可以看看这里先加和后加的区别
}
//找右边不等于val的元素
while (slowIndex <= fastIndex && nums[fastIndex] ==val)
{
fastIndex --;//同理
}//这里上述两个循环是为了寻找元素接下来是要进行元素覆盖
//nums[slowIndex--] = nums[fastIndex++];
//用右边不等于val的覆盖左边等于val
if (slowIndex < fastIndex)
{
nums[slowIndex++] = nums[fastIndex--];
}
}
return slowIndex;
};
};
其实上面的更确切的说是双向指针,双指针的话是用快指针来遍历原数组的元素,用慢指针来指代新数组中的元素然后一个一个删除即可:
class Solution
{
public:
int removeElement(vector<int> &nums, int val)
{
int slowIndex = 0;
for (int i = 0; i < nums.size(); i++)
{
// if (nums[i] == val)
//{
// nums[slowIndex] = nums[i+1];
// slowIndex++;
//}注意这里的代码问题,实质上是两种方案的选择,以上的是确定目标值与数组遍历值是否相等,
// 如果相等的情况下让即代表要删除这个相等的值,那可以把原数组下一位的值赋给慢指针的这个值即覆盖掉,缺陷在于
// 容易造成数组越界,由于存在i+1如果是数组最后一位就会造成越界访问因此会造成编译失误。
// 下面这种是判断是否不相等,若是不相等,则可以把这个数组里面的值给到慢指针的指代的值从而避免了数组越界。
if (nums[i] != val)
{
nums[slowIndex] = nums[i];
slowIndex++;
}
}
return slowIndex;
};
};