【二分查找】(板子)

二分

文章链接

视频讲解

(https://programmercarl.com/0704.%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE.html)

二分模板

注意二分可以有左闭右开,和左闭右闭的两种写法

这里用左闭右开,即l<r

左边界:区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:

while (l < r)
{
    int mid = l + r >> 1;	//(l+r)/2
    if (check(mid))  r = mid;    // check()判断mid是否满足性质
    else l = mid + 1;
}

右边界:区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:

while (l < r)
{
    int mid = l + r + 1 >> 1;	//(l+r+1)/2
    if (check(mid))  l = mid;
    else r = mid - 1;
}

如下图:左右边界指的是____________左边界 右边界____________

记忆: 有加必有减

int mid = l + r + 1 (加)>> 1;
if (check(mid)) l = mid;
else r = mid - 1  (减);

来自y总的草稿!

来自y总的草稿

思路:

有单调一定可二分,二分不一定要单调,只要满足整个区间可以根据某一性质,将区间一分为二,一边满足该性质一边不满足即可;

我们既可以找红色区间的右边界点,也可以找绿色区间的左边界点,取决于你check(mid)是用的红色性质还是绿色性质;

红色性质和绿色性质区间是互斥的,一边满足一边就不满足;

步骤:

  1. 先根据题目划分区间,写出check(mid)函数,这里的check(mid)可以是红色性质区间也可以是绿色性质区间;
  2. 根据想找的边界点,将区间划分为[l, mid]和[mid + 1, r]则令r=mid,l=mid+1,或者[l, mid - 1]和[mid, r]则令r=mid-1,l=mid
  3. 如果代码中有l=mid时,应该要mid=(1+l+r)>>1;

ps:写多了就会发现,当用check(mid)==true,如果是令l=mid时,则前面要加1。

一般二分应用于无非下面这四种情况:

  1. 找大于等于数的第一个位置 (满足某个条件的第一个数)
  2. 找小于等于数的最后一个数 (满足某个条件的最后一个数)
  3. 查找最大值 (满足该边界的右边界)
  4. 查找最小值 (满足该边界的左边界)

板子题:[力扣34](34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode))

在这里插入图片描述

//这是左闭右闭写法,l往右走,r往左走
//找右边界,让l=mid+1,即往右,不仅nums[mid]<target更新l往右,而且==时也要更新往右,才能找到右边界
//左边界同理,让r=mid-1,即往左走,不仅nums[mid]>target更新r往左,而且==时也要更新往左,才能找到左边界
class Solution {
    public int[] searchRange(int[] nums, int target) {
        int leftBorder=getLeftBorder(nums,target);
        int rightBorder=getRightBorder(nums,target);
        if(leftBorder==-2||rightBorder==-2) return new int[]{-1,-1};
        if(rightBorder-leftBorder>=2)   return new int[]{leftBorder+1,rightBorder-1};
        return new int[]{-1,-1};
    }
int getRightBorder(int[] nums,int target)
        {
            int l=0,r=nums.length-1;
            int rightBorder=-2;
            while(l<=r)
            {
                int mid=l+((r-l)>>1);
                //找右边界,不仅nums[mid]<target更新l往右,而且==时也要更新往右
                //l往右走,r往左走
                if(nums[mid]<=target){
                    l=mid+1;
                    rightBorder=l;
                } 
                else r=mid-1;                  
            }
            return rightBorder;
        }
        
    int getLeftBorder(int[] nums,int target)
        {
            int l=0,r=nums.length-1;
            int leftBorder=-2; 
            while(l<=r)
            {
                int mid=l+((r-l)>>1);
                //找左边界,不仅nums[mid]>target更新r往左,而且==时也要更新往左 
                if(nums[mid]>=target){
                    r=mid-1;
                    leftBorder=r;
                }
                else l=mid+1;
            }
            return leftBorder;
        }
  • 16
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值