移除元素
题目来源
题目描述
给你一个数组
nums
和一个值val
,你需要 原地 移除所有数值等于val
的元素。元素的顺序可能发生改变。然后返回nums
中与val
不同的元素的数量。假设
nums
中不等于val
的元素数量为k
,要通过此题,您需要执行以下操作:
- 更改
nums
数组,使nums
的前k
个元素包含不等于val
的元素。nums
的其余元素和nums
的大小并不重要。- 返回
k
。用户评测:
评测机将使用以下代码测试您的解决方案:
int[] nums = [...]; // 输入数组 int val = ...; // 要移除的值 int[] expectedNums = [...]; // 长度正确的预期答案。 // 它以不等于 val 的值排序。 int k = removeElement(nums, val); // 调用你的实现 assert k == expectedNums.length; sort(nums, 0, k); // 排序 nums 的前 k 个元素 for (int i = 0; i < actualLength; i++) { assert nums[i] == expectedNums[i]; }如果所有的断言都通过,你的解决方案将会 通过。
示例 1:
输入:nums = [3,2,2,3], val = 3 输出:2, nums = [2,2,_,_] 解释:你的函数函数应该返回 k = 2, 并且 nums 中的前两个元素均为 2。 你在返回的 k 个元素之外留下了什么并不重要(因此它们并不计入评测)。示例 2:
输入:nums = [0,1,2,2,3,0,4,2], val = 2 输出:5, nums = [0,1,4,0,3,_,_,_] 解释:你的函数应该返回 k = 5,并且 nums 中的前五个元素为 0,0,1,3,4。 注意这五个元素可以任意顺序返回。 你在返回的 k 个元素之外留下了什么并不重要(因此它们并不计入评测)。提示:
0 <= nums.length <= 100
0 <= nums[i] <= 50
0 <= val <= 100
题目限制
时间复杂度空间复杂度都是最优的O1
思路分析
这个题目和我之前做的删除有序数组中的重复项(最优解 时间复杂度O(N) 空间复杂度O(1))
两者其实非常相似,都是要原地去进行一些操作
还有这篇文章大家也可以参考一下:合并两个有序数组(双指针的应用)
只不过这个是从后往前插入,之前那个是前面插入
而我这道题目,其实也完全可以从前面开始插入
只要我先定义一个变量比如说number用来存放上一个已经插入的数字,那我可以初始化为nums[0]
再来一个insertindex来存放当前插入的位置,初始化为0
所以当我一次循环过去,在遍历的过程中,我只需要将当前位置的元素nums[i]和上一次插入的数number比较一下,如果说相等的话那就不进行任何操作,这部分就不用写了
如果说不相等的话,那我nums[insertindex]就可以是当前的这个元素,nums[i],
这样插入操作就完成了
那么之后的逻辑:
首先:插入位置后移一个单位insertindex++(可以写到插入语句里面nums[insertindex++]=nums[i])这个++就可以实现。
然后:标记当前元素,number=nums[i],也就是说把当前元素,在下一次操作的时候,用来表示“上一次插入的数”
至此,我们的程序就完成了
另外,别忘了返回一个k,用来表示有多少nums前面不重复的数字,同时也表示操作的次数,所以我初始化的时候可以先等于0,然后每次插入操作的时候就k++即可
具体代码
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int insertposition=0;
int k=0;
for(int i=0;i<nums.size();i++)
{
if(nums[i]!=val)
{
nums[insertposition++]=nums[i];
k++;
}
}return k;
}
};
注意事项
总体来说,其实这一道题目并不难做,而且还是在我们之前两道题目的基础上,大家可以去看看之前我写的两篇文章:
删除有序数组中的重复项(最优解 时间复杂度On,空间复杂度O1)-CSDN博客
这里其实我们就可以得出结论,有时候其实不需要再去新建一个容器,这样空间复杂度会大,只需要原地进行操作就可以,至于是“头插”还是“尾插”,这个就需要自己去进行判断,多看看题目的特点,以及意图。
所以说,这道题目其实是不难的,当然,如果没啥价值的话我就不写这篇文章了,在做这道题目的时候,我在碰到一个循环条件的设置的过程中,碰到了问题,接下来,我们就来探讨一下这个问题
问题描述(nums.size()的内涵)
以上,是我们的正确答案,注意观察这里的for循环语句:for(int i=0;i<nums.size();i++)
以上,是错误答案,注意观察这里的for循环语句:for(int i=0;i<=nums.size()-1;i++)
会报错,我们可以看到 "reference binding to null pointer "
也就是说,报错原因是访问了std::vector的空的元素,这是什么原因呢?
通过正确答案和错误答案的对比,我们可以发现,就只有一句话不一样
所以说:for(int i=0;i<=nums.size()-1;i++)错了
但是理论上来说,如果我输入了一个空的数组[],然后nums.size()-1就是-1,循环不进去就是了
但是事实上,size()这个函数返回的是一个无符号整数类型,表示集合或列表中的元素个数。
这样的话,如果说要对nums.size()进行-1的操作,那么当输入空数组[]的时候,0-1不会是-1,犹豫是无符号的,-1会解释成一个很大的正整数,所以在循环的时候,就会访问到一个索引值很大的地方,而输入的空数组[]就会造成访问的元素位置是空的,导致访问越界,所以在这里就报错了。