二分查找总结汇总

二分查找的注意点:

  • 区间选择(与哪些值比较 target/nums[right-1]/nums[mid-1]
  • 中位数(左中位数 or右中位数)。 for case [3,1] 能正确二分。
  • while终止条件:要保证区间选择的合法性,比如 mid-1 in [left, right)

二分查找范围

左闭右开

class Solution {
public:
    int findleft(vector<int>& nums, int target) {
        int left = 0, right=nums.size();
        while(left<right) {
            int mid = left + (right-left)/2;
            int m = nums[mid];
            if(m<target){ left = mid+1;
            } else if(m>target) { right = mid;
            } else if(m==target) { right = mid; }
        }
        if(left<nums.size()&&nums[left]==target) return left;
        else return -1;
    }
    int findright(vector<int>& nums, int target) {
        int left = 0, right=nums.size();
        while(left<right) {
            int mid = left + (right-left)/2;
            int m = nums[mid];
            if(m<target){ left = mid+1;
            } else if(m>target) { right = mid;
            // } else if(m==target) { right = mid; }
            } else if(m==target) { left = mid+1; }
        }
        // if(left<n&&nums[left]==target) return left;
        if(nums[right-1]==target) return right-1;
        else return -1;
    }
    vector<int> searchRange(vector<int>& nums, int target) {
        int l = findleft(nums, target);
        if (l==-1) return {-1, -1};
        else return {l, findright(nums, target)};
    }
};

左闭右闭

待补充

旋转数组

常见用例:[4,5,1,2,3]/[1,2]/[2]
因为区间选择时要与nums[right-1]比较,所以中位数应为左中位数。

  • 区间选择:nums[right-1]
  • 中位数:左中位数。左中位数 使得left != right for base case [2,3]
  • right-- 减而治之。收缩右边界。for case [2,2,2,2]

旋转数组的查找值II

class Solution {
public:
    bool search(vector<int>& nums, int target) {
        int left = 0, right=nums.size();
        while(left<right) {
            int mid = left + (right-left)/2;
            int m  = nums[mid], l = nums[left], r = nums[right-1];
            if (m==target) return true;
            if(m<r) {
                if(m<target&&target<=r) left = mid+1;
                else right = mid;
            } else if(m>r) {
                if(l<=target&&target<m) right = mid;
                else left = mid+1;
            } else if(m==r) {
                right--;
            }
        }
        return false;
    }
};

旋转数组的最小值II

class Solution {
public:
    int findMin(vector<int>& nums) {
        int left= 0, right=nums.size();
        while(left<right) {
            int mid = left+(right-1-left)/2;
            int l=nums[left], m=nums[mid], r=nums[right-1];
            if(m<r) right = mid+1;
            else if(m>r) left = mid+1;
            else if(m==r) right--;
        }
        return nums[left];
    }
};

寻找峰值

  • 区间选择:比较 nums[mid-1] ?= nums[mid]
  • 结束条件:left<right-1。 保证mid-1有效for base case [2] 。 而且峰值必然存在,所以最终结果的[left,right)有且仅有一个元素。
    常见用例:[1,2], [1,2,1,2]
class Solution {
public:
    int findMin(vector<int>& nums) {
        int left= 0, right=nums.size();
        while(left<right) {
            int mid = left+(right-1-left)/2;
            int l=nums[left], m=nums[mid], r=nums[right-1];
            if(m<r) right = mid+1;
            else if(m>r) left = mid+1;
            else if(m==r) right--;
        }
        return nums[left];
    }
};

二维数组查找

  • 中位数的选择:nums[x][y] ?= target
  • 区间选择:x++, y--

区间收敛示意图

class Solution {
public:
    bool Find(int target, vector<vector<int> > array) {
        int nx = array.size();
        int ny = array[0].size();
        int x=0, y=ny-1;
        while(x<nx && y>=0) {
            int val = array[x][y];
            if(val<target) { x++;
            } else if(val>target) { y--;
            } else if(val==target) { return true; }
        }
        return false;
    }
};

K个最接近的数

  • 基本思路:二分法 + 滑动窗口 [nums[mid],nums[mid+k])
  • 区间选择:x-nums[mid] ?= nums[mid+k]
    区间选择示意图
class Solution {
public:
    vector<int> findClosestElements(vector<int>& arr, int k, int x) {
        vector<int>& nums = arr;
        vector<int> res;
        int left=0, right=nums.size()-k;
        while(left<right) {
            int mid = left+(right-left) /2;
            if(x-nums[mid]>nums[mid+k]-x) { left = mid+1;
            } else { right = mid; }
        }
        vector<int> res(nums.begin()+left, nums.begin()+left+k);
        return res;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值