移动零-相关算法

最近抽出时间leetcode刷了一些算法,突然发现很多很简单的题慢慢就引申出好几个问题,记录一下方便汇总

https://leetcode-cn.com/problems/move-zeroes/   283  移动零

https://leetcode-cn.com/problems/remove-element/    27题   移除元素

https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/   26题   删除排序数组中的重复项

https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array-ii/  80题  删除排序数组中的重复项II

这四个题目其实是一类题,感兴趣可以先去连接中看题目。

这里直接从第一个 移动零说起


给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

示例:

输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
说明:

必须在原数组上操作,不能拷贝额外的数组。
尽量减少操作次数。


其实就是要去将0 移动到末尾

思路1:我们将非0元素顺序保存到一个 数组中,然后将原数组从头填充,后面补0, 空间复杂度 O(n) 不符合题意。

优化:我们如何能将 复制数组 这一步 变成一个 O(1) 的空间消耗

延伸出思路2:

我们将 非0 元素向前移动,0 向后移动,利用遍历本身的顺序性来替换掉中间存储,所以我们需要两个

指针  分别来记录 0 的下标位置,和当前迭代位置

while 换成 for 可能 更简洁,当然我们也可以通过swap 来实现

void moveZeroes(vector<int>& nums) {
        // 两个指针 i,j 同时开始,i 遇到0 停了,j 后移,然后替换
        int n = nums.size();
        if  (n < 2) return ;
        int i = 0;
        int j = 0;
        while(j < n && i < n){
            if(nums[j] != 0){
                //可以用swap 代替
                nums[i] = nums[j];
                if(i != j){
                    nums[j] = 0;
                }
                i++;
            }
            j++;
            
        }
    }

 

基于个基本算法 我们看一下下一题 “移除元素” 

其实本质就是 将 “移除0” 变为了 “移除x” + 返回新长度

移除x 判断很简单,新长度,其实就是 最后 移动到 后面的x 的所在位置,就是j

看一下代码 ,有没有发现和第一题是一模一样的

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

删除排序数组中的重复项 也是很类似的,但是这里是排序数组,移除0 变为移除重复项  

如何表述重复项:  我们找到不重复项就很容易表述重复项了  nums[i] != nums[j]

j++ 是因为 去除的是重复项 要覆盖 掉的是重复元素的第二位,换言之,你要保留下来一个 ,j+1 同理

 int removeDuplicates(vector<int>& nums) {
        int i=0;
        int j=0;
        
        int n=nums.size();
        if (n < 2) return n;
        for(i=0;i<n;i++){
            if(nums[i] != nums[j]){
                j++;
                nums[j] = nums[i];
            }
            
        }
        return j+1;
    }

接着看一下升级版的 “删除排序数组中的重复项 II”

条件从 上一题的 使得每个元素最多出现一次,扩展到了“最多出现两次”

所以我们要构造出 “两次” 这个条件 然后不停的去替换,这个有点不太好理解。

我们将最多出现两次 转换为 超过两次 的元素应该向后移动,以保证最多出现两次,也就是低于两次的 不停覆盖前面就行了,只是这个覆盖位置 有可能是当前所在位置。

int removeDuplicates(vector<int>& nums) {
        //[0,0,1,1,1,1,2,3,3]  => [0,0,1,1,2,3,3]
        //思路还是 移动 0 元素的思路
        //这样思考,我们覆盖 每个位置 多余的 ( >2的 )元素,覆盖为后面元素,同样也会让后面的接着覆盖重复的
        int i=1,j=1;
        int n = nums.size();
        if (n < 3) return n;
        int count = 1;
        for(i=1;i<n;i++){
            if(nums[i] == nums[i-1]){
                count ++;
            }else{
                count = 1;
            }
            //这个位置是一个经典, 类比 移除重复元素中  nums[i] != nums[j]  移动 0  中 的 nums[i] != 0
            if(count <= 2){
                nums[j] = nums[i];
                j++;
            }
        }
        return j;
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值