• 二分查找 【模板+中间值问题】


    😃前言

    二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。它要求元素具有有序性,或者具有跟有序性类似的性质,能够不断地缩小查找范围。

    查找操作主要有一下几点:

    1、确定需要查找的值,设立基准值(一般是中间值),设立判断条件,判断基准值是否满足某种性质

    2、以基准值为边界,划分左右两个区间

    3、根据判断条件是否成立,分析结果可能存在的区间,更新查找范围

    4、重复以上操作,直到区间不存在(左边界大于右边界)或查找到需要的值时,查找结束


    😕二分查找动图演示

    在这里插入图片描述


    😴代码模板

    我们这里以整数的有序序列为例,演示二分查找的代码和需要注意的事项。整数二分的代码分为两种情况:

    1. 判断条件成立时新区间需要更新在左半区间,即[l, mid]
    1. 判断条件成立时新区间需要更新在右半区间,即[mid, r]

    需要注意的是不管哪种情况,条件成立都需要将取到mid,因为mid可能为答案

    bool check(int x) {/* ... */} // 检查答案是否满足某种性质
    
    // 查找左区间
    // 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:条件成立,新区间在[l, mid]
    int SearchLeft(int l, int r)
    {
        while (l < r)
        {
            int mid = l + r >> 1;
            if (check(mid)) r = mid;    // check()判断mid是否满足性质
            else l = mid + 1;
        }
        return l;
    }
    
    // 查找右区间
    // 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:条件成立,新区间在[mid, r]
    int SearchRight(int l, int r)
    {
        while (l < r)
        {
            int mid = l + r + 1 >> 1;
            if (check(mid)) l = mid;
            else r = mid - 1;
        }
        return l;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    ❗️ 使用哪个模板问题 ❗️

    决定使用哪个模板,是根据check(mid)来决定的:

    check(mid)就是判断答案在左半区间还是右半区间

    1、如果check(mid)是判断答案在左半区间,使用SearchLeft模板(mid不需要+1):

    如果在条件成立,说明答案在左半区间,也就是[l, mid],新区间为[l, mid],区间更新方式为r = mid
    如果条件不成立,说明答案在右半区间,也就是[mid + 1, r],新区间为[mid + 1, r],区间更新方式为l = mid + 1

    2、如果check(mid)是判断答案在右半区间,使用第二个模板(mid需要+1):

    如果在条件成立,说明答案在右半区间,也就是[mid, r],新区间为[mid, r],更新条件为l = mid
    如果条件不成立,说明答案在左半区间,也就是[l, mid - 1],新区间为[l, mid - 1],更新条件为r = mid - 1


    💢 mid为何+1问题 💢

    在这两种情况中,区间划分好理解,不就是一左一右、一加一减嘛。但是在mid为何需要加1可能会让很多人疑惑,我在学习的时候也是迷糊了好久。现在将从大佬那里总结到的经验分享一下:

    1、在SearchLeftmid 为何不需要 +1

    设左右两个边界只相差1,即l = r - 1时,如果 mid + 1mid 向下取整 ,即mid == r ,如果条件刚好成立的话,就会导致更新完区间后r还是等于mid,等于没有更新,这时候就是一个死循环了。所以mid 不需要 +1,进行向上取整,让mid == l,破坏死循环的条件。

    2、在SearchRightmid为何需要 +1

    设左右两个边界只相差1,即l = r - 1时,当mid不 +1 时mid是向上取整的,所以mid == l,如果条件刚好成立的话,就会导致更新完 区间后l还是等于mid,等于没有更新,这时候就是一个死循环了。所以需要mid+1进行向下取整,让mid == r,破坏死循环的条件。

    大佬原话:

    对于mid + 1与否我觉得是为了让区间平分
    mid = left + right >> 1; 这里mid是上中位数
    mid = left + right + 1 >> 1; 这里mid是下中位数
    如果取left = mid, 即[mid, right], 则mid取下中位数才能平分区间
    如果取right = mid, 即[left, mid], 则mid取上中位数才能平分区间


    完结散花🌈🌈🌈
    在这里插入图片描述

  • 相关阅读:
    【JAVA学习笔记】64 - 坦克大战1.4,限制坦克发射子弹,敌方击中我方坦克爆炸
    CocosCreator 面试题(十二)Cocos Creator Label 的原理以及如何减少Drawcall
    [附源码]Python计算机毕业设计SSM奖助学金评审系统(程序+LW)
    ES6 不完全手册(上)
    《DevOps围炉夜话》- Pilot - CNCF开源DevOps项目DevStream简介 - feat. PMC成员胡涛
    TCP 要“凉“,UDP 要崛起 !
    深入探讨 Presto 中的缓存
    AI绘图提示词Stable Diffusion Prompt 笔记
    【Java】防沉迷实名认证系统接口测试代码(已全示例通过)
    【每日一题】1041. 困于环中的机器人
  • 原文地址:https://blog.csdn.net/weixin_54202947/article/details/127696621