二分查找模板(y总版)

本文介绍了两种二分查找模板,版本一和版本二,分别适用于不同类型的区间划分策略。通过实例分析LeetCode题目,强调了check函数在选择边界时的重要性,并结合实际场景进行解释。适合理解和实践二分查找算法的开发者。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

二分查找模板

首先先上模板,y总(B站大雪菜)总结出来了两套二分的代码,基本能解决九成左右的二分问题.二分在特别的边界问题上可以选用此模板作为参考。当然在一些简单的边界问题上,可以自己思考边界来自己写代码。

版本一

当我们将区间[l, r]划分成[l, mid][mid + 1, r]时,其更新操作是r = mid或者l = mid + 1;计算mid时不需要加1

int bsearch_1(int l, int r)
{
    while (l < r)
    {
        int mid = l + r >> 1; //除2 操作
        if (check(mid)) r = mid; //check函数即为边界的选择
        else l = mid + 1;
    }
    return l; //l r都可以,跳出while r = l
}

版本二

当我们将区间[l, r]划分成[l, mid - 1][mid, r]时,其更新操作是r = mid - 1或者l = mid;此时为了防止死循环,计算mid时需要加1

int bsearch_2(int l, int r)
{
    while (l < r)
    {
        int mid = l + r + 1 >> 1; //注意+1操作
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    return l;
}

代码解释(来自B站大雪菜老师讲解,加些自己的理解)

在这里插入图片描述

如图我们把一个闭区间[l, r],分为左右两端(我们要明白,对于二分算法来说,数据在某种意义上是有序的,或者是直接有序,例如非递减。或者是以其他的形式有序,例如旋转有序。)再者,因为二分是抛弃一段,从另一段中继续寻找答案,所以我们就可以把整个过程一直当做是两段中选一段。其实分段的这个过程就是check()函数的确定。

① ① 对于模板一,我们就好比在查找图中绿色段中的左端点。如果我们的mid在红色段,肯定是不符合查找需求的,让l = mid + 1。如果mid是在绿色段,即符合我们的查找需求,但不是结束点,让r = mid
② ② 对于模板二,我们就好比在查找图中红色段中的右端点。如果我们的mid在绿色段,肯定是不符合查找需求的,让r = mid - 1。如果mid是在红色段,即符合我们的查找需求,但不是结束点,让l = mid。此时我们需要注意一点。即陷入死循环,举例:我们取下标l = r - 1,如果我们在计算时选择下取整(直接除2),m = (l + r) / 2 = (2 * l + 1) / 2 = l,我们再次更新l = mid,就陷入了死循环。

在这里插入图片描述

例题

LeetCode 69. x 的平方根

class Solution {
public:
    int mySqrt(int x) {
        int l = 0, r = x;
        while(l < r)
        {
            int mid = (long long)l + r + 1 >> 1;  //check函数 mid * mid <= x
            if(mid <= x / mid) l = mid;
            else r = mid - 1;

        }
        return l ;
    }
};

此时有读者按照上面的讲解,就在想,check函数能不能使用 mid * mid >= x。当然这种想法是没问题的。现学现用,但是我们来看这个题目的样例二,明显的给我们表明了,我们对于不能整数开平方的数来说,需要舍去小数,即下取整。当我们使用 mid * mid >= x 当x是整数的开平方数的话,自然是没有问题的,但当x不能被整开平方,我们的这个check最后取得结果就是上取整,不符合题意。
所以当我们在选择check条件的时候也不是随便两个模板换着用,一切以符合题意为主。

LeetCode 35. 搜索插入位置

这个题目就是简单的寻找有序数组中第一个大于等于target的位置,c++STL中有lower_bound()

return lower_bound(nums.begin(),nums.end(),target) - nums.begin();
class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        if(nums.empty() || nums.back() < target) return nums.size();
        int l = 0, r = nums.size() - 1;
        while(l < r)
        {
            int mid = (l + r) / 2;
            if(nums[mid] < target) l = mid + 1;
            else r = mid;
        
        }
        return l;
    }
};

这个题目就是边界选择,而且这个题目的check是可以改变的,画个图理解l, r的变化。

觉得文章有用,点个赞,点个收藏,支持一下

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BiuPsYao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值