【Day1】 二分 + 双指针

【Day1】 二分 + 双指针

704. 二分法

  1. 左闭右闭(迭代 + 循环):

    class Solution {
    private:
        int res = -1;
        int divid(vector<int>& nums, int l, int r, int target){
            // 迭代终止条件
            if(l > r) {
                return res;
            }
            int m = (l + r) / 2;
            if(nums[m] > target) {
                res = divid(nums, l, m - 1, target);
            }
            else if(nums[m] < target){
                res = divid(nums, m + 1, r, target);
            }
            else{
                res = m;
            }
            return res;
        }
    public:
        int search(vector<int>& nums, int target) {
            int len = nums.size();
            return divid(nums, 0, len - 1, target);
        }
    };
    
    
    // 2. 循环
    class Solution {
    public:
        int search(vector<int>& nums, int target) {
            int res = -1;
            int l = 0;
            int r = nums.size() - 1;
            while(l <= r) {
                int m = (l + r) / 2;
                if(nums[m] > target) {
                    r = m - 1;
                }
                else if(nums[m] < target){
                    l = m + 1;
                }
                else{
                    res = m;
                    break;
                }
            }
            return res;
        }
    };
    
  2. 左闭右开(仅循环):

    class Solution {
    public:
        int search(vector<int>& nums, int target) {
            int res = -1;
            int l = 0;
            int r = nums.size();
            while(l < r) {
                int m = l + ((r - l) / 2);
                if(nums[m] > target) {
                    r = m;
                }
                else if(nums[m] < target){
                    l = m + 1;
                }
                else{
                    res = m;
                    break;
                }
            }
            return res;
        }
    };
    
  3. 注意事项:

    int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2

  4. 感想:

    右开的时候:因为不含最右侧的元素,所以 ——

    • 起始的时候要多往右探一位
    • 右指针所指是虚的,不是真正会被比较的元素,所以l==r的情况下,其实被比较的子数据已经空了,不必再进行切分了,所以这里while中不再有此条件。

27. 移除元素

  1. 暴力循环:

    注意:这里的循环终止条件要考虑到后面的val元素长度,否则会陷入死循环

    // 1. while
    class Solution {
    public:
        int removeElement(vector<int>& nums, int val) {
            int len = nums.size();
            int same = 0; // 相同元素计数
            int i = 0; // 当前元素指针
            while(i < len - same){
                if(nums[i] == val){
                    // 逐个往后交换元素,给挪到最后(其它已就位val元素的前面)
                    for(int j = i + 1; j < len - same; j++){
                        int trans = nums[j];
                        nums[j] = nums[j - 1];
                        nums[j - 1] = trans;
                    }
                    same ++;  // 相同元素 +1
                }else{
                    i++;
                }
            }
            return len - same;
        }
    };
    // 2. for循环
          for(int i = 0; i < len - same; i++){
                if(nums[i] == val){
                    // 逐个往后交换元素,给挪到最后(其它已就位val元素的前面)
                    for(int j = i + 1; j < len - same; j++){
                        int trans = nums[j];
                        nums[j] = nums[j - 1];
                        nums[j - 1] = trans;
                    }
                    same ++;  // 相同元素 +1
                    i--; // 指针回退一格(因为后面的元素顶上来了)
                }
            }
    
  2. 直接交换法

    • 思路: val元素直接与最后的元素进行一个交换(交换时进行二次判断、当最后的元素已经是val,则计数+1、转为同其前面的元素进行交换,以此类推)
    • 注意: 这里题目中不要求保留原数组的元素顺序,所以可以用这种方法实现,若有此要求则不可用。
    class Solution {
    private:
        // Func: 数组元素交换位置
        int change(vector<int>& nums, int l, int r) {
            int trans = nums[l];
            nums[l] = nums[r];
            nums[r] = trans;
            return 0;
        }
    
    public:
        int removeElement(vector<int>& nums, int val) {
            int len = nums.size();
            int same = 0; // 相同元素计数
            for (int i = 0; i < len - same; i++) {
                if (nums[i] == val) {
                    // 末尾是val元素,则直接跳过、节省时间
                    // 这里要同时考虑数组访问不可溢出
                    while (len > same && nums[len - same - 1] == val) {
                        if(i == len - same - 1){
                            break;// 末尾元素不是i元素本身时才跳过
                        }
                        same++;
                    }
                    change(nums, i, len - same - 1);
                    same++;
                }
                // 由于末尾的元素已经判断过是否val,所以换过来的元素必然非val,故不再进行指针回退
            }
            return len - same;
        }
    };
    
  3. 双指针法

    思路: 这个大概就是一个人在前面去找所有不是val的元素,另一个元素留守后方规规矩矩一步一格去标记所有非val元素的最终位置(因为val元素相当于是不占前面的位置的),所以就是慢指针不断接收快指针送过来的元素就完事了,不用再进行交换、直接覆盖,所以可以写得很粗暴。

    class Solution {
    public:
        int removeElement(vector<int>& nums, int val) {
            int l = 0, r = 0;
            while(r < nums.size()){
                // r负责找非val元素(注意防止溢出)
                if(nums[r] == val){
                    r++;
                }else{
                    nums[l++] = nums[r++];// 快指针把非val元素丢给慢指针
                }
            }
            return l;
        }
    };
    

    双指针法练习:

  4. 977. 有序数组的平方

    class Solution {
    public:
        vector<int> sortedSquares(vector<int>& nums) {
            int len = nums.size();
            int left = 0, right = len - 1, cur = len - 1;
            vector<int> res(len,0);
            while(left <= right){
                int mul_l = nums[left] * nums[left];
                int mul_r = nums[right] * nums[right];
                if(mul_l > mul_r) {
                    res[cur--] = mul_l;
                    left++;
                }
                else{
                    res[cur--] = mul_r;
                    right--;
                }
            }
            return res;
        }
    };
    
  5. 283. 移动零

    class Solution {
    public:
        void moveZeroes(vector<int>& nums) {
            int l = 0, r = 0;
            int len = nums.size();
            while(r < len){
                if(nums[r] != 0){
                    nums[l++] = nums[r++];
                }
                else{
                    r++;
                }
            }
            while(l < len){
                nums[l++] = 0;
            }
        }
    };
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值