双指针算法-day7 (原地修改)

1.移除元素

题目

解析

1.解析

  • 左指针用来更新数组,右指针查找元素;
  • 若右指针元素不等于目标值,左指针不断更新数组;若等于目标值,跳过该值即可;
  • 时间复杂度:O(n);空间复杂度:O(1);

代码

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        // 时间复杂度:O(n)
        // 空间复杂度:O(1)

        int l = 0;
        for(int r = 0;r < nums.size();r ++){
            if(nums[r] != val){
                nums[l ++] = nums[r];
            }
        }

        return k;
    }
};

2.删除有序数组中的重复元素

题目

解析

1.解析

  • 由于数组是非递减的,所以只用比较当前数字和前一个数字是否相等,如果相等就直接跳过,不相等就让左指针更新数组即可;
  • 自己写的是用哈希表,可以解决非有序数组情况,代码如下;
  • 时间复杂度:O(n);空间复杂度:O(1);

代码

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        // 时间复杂度:O(n)
        // 空间复杂度:O(1)

        int n = nums.size();

        int l = 1;
        for(int r = 1;r < n;r ++){
            if(nums[r] != nums[r - 1]) nums[l ++] = nums[r];
        }

        return l;
    }
};
  • 哈希表解决非有序情况:
class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        // 时间复杂度:O(n)
        // 空间复杂度;O(非重复元素个数)

        int n = nums.size();

        unordered_map<int,int> p;

        int l = 0;
        for(int r = 0;r < n;r ++){
            int x = nums[r];

            if(p[x] == 0) {
                p[x] ++;
                nums[l ++] = nums[r];
            }else {
                continue;
            }   
        }

        return l;
    }
};

3.删除有序数组中的重复元素II

题目

解析

1.解析

  • 如果数组只有1 / 2个元素,直接返回;
  • 这题和上题的区别就在于每个元素最多存两个,思路稍微改一下就可以,把当前数与左指针左边第二个数比较,不相等就更新数组,相等就跳过;
  • 时间复杂度:O(n);空间复杂度:O(1);

代码

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        // 时间复杂度:O(n)
        // 空间复杂度:O(1)

        int n = nums.size();

        if(n == 1 || n == 2) return n;

        int l = 2;
        for(int r = 2;r < n;r ++){
            if(nums[r] != nums[l - 2]) nums[l ++] = nums[r];
        }

        return l;
    }
};

4.移动0

题目

解析

1.解析

  • 核心思想:左指针从头更新数组,右指针的数若不等于0,就将其与左指针的数交换;
  • 时间复杂度:O(n);空间复杂度:O(1);

代码

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        // 时间复杂度:O(n)
        // 空间复杂度:O(1)

        int n = nums.size();

        int l = 0;
        for(int r = 0;r < n;r ++){
            if(nums[r] != 0) swap(nums[l ++],nums[r]);
        }
    }
};

5.按奇偶排序数组

题目

解析

1.解析

  • 核心思想:左指针从头更新数组,右指针的数为偶数,就将其与左指针的数交换;
  • 时间复杂度:O(n);空间复杂度:O(1);

代码

class Solution {
public:
    vector<int> sortArrayByParity(vector<int>& nums) {
        // 时间复杂度:O(n)
        // 空间复杂度:O(1)

        int n = nums.size();

        int l = 0;
        for(int r = 0;r < n;r ++){
            if(nums[r] % 2 == 0) swap(nums[l ++],nums[r]);
        }

        return nums;
    }
};

6.按奇偶排序数组II

题目

解析

1.解析

  • 核心思想:一组一组 偶数+奇数 的去更新数组;
  • 找到偶数下标最左边的奇数,奇数下标最左边的偶数,进行交换;
  • 时间复杂度:O(n);空间复杂度:O(1);

代码

class Solution {
public:
    vector<int> sortArrayByParityII(vector<int>& nums) {
        // 时间复杂度:O(n)
        // 空间复杂度:O(1)

        int n = nums.size();

        int l = 0,r = 1;

        // 核心:一组一组 偶数+奇数 地向后更新数组
        while(l < n){
            if(nums[l] % 2 == 0){ // 找到偶数下标最左边的奇数
                l += 2;
            }else if(nums[r] % 2 == 1){ // 找到奇数下标最左边的偶数
                r += 2;
            }else{
                swap(nums[l],nums[r]);// 交换
                l += 2;
                r += 2;
            }
        }

        return nums;
    }
};

7.复写0

题目

解析

1.解析

  • L:   从左边遍历元素,碰到 0 时则需要将 R-- 表示舍弃一个元素为复制 0 腾出空间。
  • R:从数组尾部开始,指向下一个即将被抛弃的元素。
  • K:指向下一个被复制元素需要去的位置。
  • 难点:当 l == r 且当前元素为 0,既要保留(复写为2个0),又要舍弃1个0。所以最后还剩下一个0,直接放到填充位置;
  • 时间复杂度:O(n);空间复杂度:O(1);

代码

class Solution {
public:
    void duplicateZeros(vector<int>& arr) {
        // 时间复杂度:O(n)
        // 空间复杂度:O(1)

        int n = arr.size();

        // l指向要保留的元素,r指向要删除的元素,k指向后方要填充的位置
        int l = 0,r = n-1,k = n-1;

        // 左指针找到一个0,右指针就要左移一位,即舍弃一个元素
        for(l = 0;l < r;l ++){
            if(arr[l] == 0) {
                r --;
            }
        }

        // 当 l == r 且当前元素为 0,既要保留(复写为2个0),又要舍弃1个0
        // 所以最后还剩下一个0,直接放到填充位置
        if(l == r && arr[l] == 0) arr[k --] = arr[r --];

        while(r >= 0){
            if(arr[r] == 0) {
                arr[k --] = 0;// 复写0
            }
            arr[k --] = arr[r --];
        }
    }
};

8.总结

  • 原地修改双指针步骤:
  • 首先确定左右指针位置和移动方向;
  • 其次,确定更新方式,一般有3种:
    • 1.左指针更新数组,右指针移动寻找满足条件的元素 ,将右指针指向的数直接赋值给左指针。(适用于可以舍弃不满足条件的元素);
    • 2.将左指针指向的数与右指针指向的数交换。(适用于将满足条件的数提前的题目);
    • 3.左右指针同频移动,共同寻找不符合条件的数组对,进行交换;
  • 复写0 这题比较有思想深度,用到了三指针,得多想多模拟;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值