[Leetcode] 27. Remove Element

Description:
Given an array and a value, remove all instances of that value in-place and return the new length.

Do not allocate extra space for another array, you must do this by modifying the input array in-place with O(1) O ( 1 ) extra memory.

The order of elements can be changed. It doesn’t matter what you leave beyond the new length.

Example:

Given nums =[3,2,2,3], val = 3,

Your function should return length = 2, with the first two elements of nums being 2.

Language: C++

解法一:
虽然题目要求in-place,但是可以先考虑如果不要求in-place的话,可以怎么做。解法一没有in-place。思路是遍历一遍数组,把不等于val的数存到新申请的vector中,那么这就是新的数组了,它的size就是不等于val的元素的个数。
Time: O(n) O ( n )
Space: O(n) O ( n )

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int res = 0;
        vector<int> arr;
        for(int i = 0; i < nums.size(); i++){
            if(nums[i]!=val)
                arr.push_back(nums[i]);
        }
        for(int i = 0; i < arr.size(); i++)
            nums[i] = arr[i];
        res = arr.size();
        return res;
    }
};

解法二:
基本思想:运用两个指针。相当于用i来遍历,j来控制使得[0, j)范围内的数都不等于val。弄清楚含义以后,可以知道,j初值可设为0。(也就是如果含义是j来控制使得[0, j]范围内的数都不等于val,那么j的初值就应该设为-1) 当遍历到的数不等于val时,满足条件,将nums[i]赋给nums[j],然后j++。与下面解法三不同的是:这里不论nums[i]是否与val相等,i都要向前加1。
要仔细读题,题目中“The order of elements can be changed. It doesn’t matter what you leave beyond the new length.”说不care有效长度后面的元素是什么,因此不用考虑数组后面无效的部分,只需要把前面解决就行,也就是把不等于val的数都挪到前面就行,后面是什么样的不用管。不过我还是给出了考虑到in-place的同时使得数组变成一个不含val的数组,也就是真正意义上把val给删除了。
Time: O(n) O ( n )
Space: O(1) O ( 1 )

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int res = 0;
        int j = 0;//保证在[0...j)的元素都不包含要删除的val
        for(int i = 0; i < nums.size(); i++){
            if(nums[i] != val)//把不等于val的往前挪,最后遍历完后erase后面的部分
                nums[j++] = nums[i];
        }
        res = j;//最后一次j++了之后变成了现在的j,所以不等于val的范围在[0,j-1]共j个元素 这里注意哦!

        nums.erase(nums.begin()+j-1, nums.end());//*题目说超过有效范围的部分不care是什么,这里我进一步把后面的给删了,这一句在这道题是不必须的
        return res;
    }
};

算法细节:

  • 关键点在于首先搞清楚i,j或其他语句的含义,然后之后的代码一定严格遵循这些含义
  • 在用vector的erase方法的时候,要注意几种错误写法:

    nums.erase(j, nums.end());
    //错误原因:j是int, end是迭代器
    nums.erase(j, nums.size());
    //错误原因:j是int, size是sizetype类型,这里传入的是j的引用,强制转换size也不行

    特别要注意,erase可以删除某个position的元素(一个参数),或者某个范围内的元素(两个参数,且是左闭右开,[first,last))
    正确用法示例:(来源:Resource: cplusplus)

    // erase the 6th element
    myvector.erase (myvector.begin()+5);
    
    // erase the first 3 elements:
    myvector.erase (myvector.begin(),myvector.begin()+3);

解法三:
基本思想:两个对撞指针。
这种方法尤其适合数组中需要删除的val值元素很少的时候。因为用解法二,我们有可能进行了多次不必要的交换。
思路:(来源:Leetcode Solution)
When we encounter nums[i] = val, we can swap the current element out with the last element and dispose the last one. This essentially reduces the array’s size by 1.
当从左向右遍历到的nums[i]等于val时,我们可以将它与最后一个元素交换,然后再dispose掉最后一个元素。这一步也把数组的size减了1。
Note that the last element that was swapped in could be the value you want to remove itself. But don’t worry, in the next iteration we will still check this element.
需要注意的是当最后一个元素被换到当前位置的时候,有可能还是一个需要删除的数,因此我们下一趟的判断还要对这个元素进行判断,也就是遍历指针还不能往前挪。
Time: O(n) O ( n )
Space: O(1) O ( 1 )

public int removeElement(int[] nums, int val) {
    int i = 0;//保证[0,i)范围内的值都是有效的,即不等于val的
    int n = nums.length;
    while (i < n) {
        if (nums[i] == val) {
            nums[i] = nums[n - 1];
            // reduce array size by one
            n--;//记得把size减1
        } else {
            i++;//只有在当前的nums[i]!=val的情况,i才向前+1
        }
    }
    return n;
}

算法细节:

  • 注意到我们的这个解法只有在当前的nums[i]!=val的情况,i才向前+1,这是与解法二不同的地方,因为我们要保证[0,i)范围内的值都是有效的,即不等于val的(永远stick to 语句的含义),这也是我们用while不用for的原因。
  • 启发:我在看到delete或者remove的时候,总是觉得一定要把等于val的值真正的从数组中除掉,但实际上题目并没有要求,那么怎么才能体现有效部分的长度呢?第一种方法是利用语句含义,也就是解法二中的保证在[0…j)的元素都不包含要删除的val,和解法三中的保证[0,i)范围内的值都是有效的;第二种是当不满足有效条件时,即当前的值等于val时,n减1,n的初值为数组长度。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值