移除元素(最优解)

移除元素


题目来源

力扣27题 移除元素


题目描述

给你一个数组 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;
    }
};

注意事项

总体来说,其实这一道题目并不难做,而且还是在我们之前两道题目的基础上,大家可以去看看之前我写的两篇文章:

合并两个有序数组(双指针的应用)-CSDN博客

删除有序数组中的重复项(最优解 时间复杂度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会解释成一个很大的正整数,所以在循环的时候,就会访问到一个索引值很大的地方,而输入的空数组[]就会造成访问的元素位置是空的,导致访问越界,所以在这里就报错了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值