leetcode 27.删除元素

要求删除给定数组中和val大小一样的元素,返回删除后数组的大小,同时原数组中的元素得删除掉。这表明不能直接一个遍历,直接返回原数组大小-元素个数。

看到这题我的第一个思路:

新建一个数组,把原数组中符合条件的元素添加进去。再让原数组等于新数组。

代码如下:

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        vector<int> res;
        for(auto x: nums){
            if(x!=val){
                res.push_back(x);
            }
        }
        nums = res;
        return nums.size();
}
};

 思路非常简单,暴力。时间复杂度为O(N)空间复杂度为O(N)

一般要新建一个vector再将新vector赋值给老vector的题都可以使用双指针法,双指针法可以将空间复杂度降为O(1)。

双指针法:

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int left = 0,right = nums.size()-1,count =0;
        while(left<=right){
            if(nums[left]==val){
                swap(nums[left],nums[right]);
                right--;
                count++;
            }else{
                left++;
            }
        }
        return nums.size()-count;

    }
};

因为这题只考虑返回的原数组前k(原数组个数-和val一样的元素个数)个值是否和要求一样,并且没有排序要求,所以可以直接使用swap在原数组上通过双指针将与val大小一样的元素移动到数组尾部。如果数组尾部也是一个和val大小一样的元素,那swap这两个数的意义在于用count记录了swap的次数,swap过后通过right--来将指针从数组尾部往前进一个,代表着swap过来的数不会在被我们考虑到。而被swap到前面的数如果value也与val一样的话,则会将它和排在数组倒数第二位置的数交换,而不会进入两个数都等于val的swap死循环。

重读完题干后我发现这道题的空间复杂度本来的要求是O(1),不知道我的第一种方法是怎么通过的。看了carl哥的文章(代码随想录),自己重写了一个符合空间复杂度为O(1)的brute force解法。

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        //space should be O(1)
        int count =0;
        int i=0;
        while(i<nums.size()){
            //如果i位置的元素与val大小一样
            if(nums[i]!=val){
                i++;
            }else{
                int j = i;
                while(j<nums.size()-1){
                    nums[j]=nums[j+1];
                    j++;
                }
                nums[nums.size()-1]=-1;
                count++;
            }
        }
        
        return nums.size()-count;
    }
};

有一个不足之处是当元素大小为val时我将整个后面部分往前移动一步时 ,给最后一个元素补充的数值为-1, 如果测试数据中存在-1的元素时则会陷入死循环。

优化:每当把元素整体往前提一个的时候,直接把数组的总长度缩小1个

class Solution {
public:
    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];
                }
                size--;
                i--;
            }
        }
        return size;
    }
};

这个方法不存在元素得大于0的问题,因为使用size作为遍历的区间。当我取出元素时,会将size-1所以实际上只用size内的部分。另一个小技巧则是在for循环中可以使用i--来再次确定提到前面的元素是不是与val相当需要取出的情况,这个技巧我之前是没有见过的。

看完carl哥的双指针法思路写出的代码:

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int slow=0,fast=0,count=0;
        while(fast<nums.size()){
            if(nums[fast]!=val){
                nums[slow]=nums[fast];
                fast++;
                slow++;
            }else{
                //fast指向的数和val相同
                fast++;
                count++;
            }
        }
        return nums.size()-count;
    }
};

代码随想录中简化后的代码:

// 时间复杂度:O(n)
// 空间复杂度:O(1)
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int slowIndex = 0;
        for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
            if (val != nums[fastIndex]) {
                nums[slowIndex++] = nums[fastIndex];
            }
        }
        return slowIndex;
    }
};

化简的代码思路是:无论如何fast指针要往前移动,放在for循环里,而slow指针只有在fast指向不为val时才移动,同时更新slow。使用后置运算符实在是妙!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值