分享丨【算法题单】二分算法(二分答案/最小化最大值/最大化最小值/第K小)
426762
2023.12.22
2025.11.07
发布于 未知归属地

二分题单 二分查找 二分算法 二分入门 二分题目 力扣二分 leetcode 二分

图:闭区间二分循环结束时的左右指针位置(查找第一个

刷题建议:初次刷题可以只刷难度低于 分的题目。难度更高的题目常常结合其他算法(数据结构、图论等),等学会其他算法再来刷本题单。

一、二分查找

讲解:二分查找 红蓝染色法【基础算法精讲 04】

为递增(非递减)数组,长为

需求写法如果不存在
的第一个元素的下标结果为
的第一个元素的下标结果为
的最后一个元素的下标结果为
的最后一个元素的下标结果为
需求写法
的元素个数
的元素个数
的元素个数
的元素个数

注意 互为补集,二者之和为 同理。

§1.1 基础

§1.2 进阶

部分题目需要先排序,然后在有序数组上二分查找。

思维扩展

二、二分答案

“花费一个 的时间,增加了一个条件。” —— 二分答案

§2.1 求最小

题目求什么,就二分什么。

答疑

:如何把二分答案与数组上的二分查找联系起来?

:假设答案在区间 中,我们相当于在一个虚拟数组 中二分找第一个(或者最后一个)值为 。这同样可以用红蓝染色法思考。

:有些题目,明明 可以是答案,但却不在初始二分区间中。比如闭区间二分初始化 (或者开区间 ),这不会算错吗?

:不会算错。注意「答案所在区间」和「二分区间」是两个概念。想一想,如果二分的 循环每次更新的都是 ,那么最终答案是什么?正好就是 。一般地,如果一开始就能确定 一定可以满足题目要求,那么 是不需要在二分区间中的。换句话说,二分区间是「尚未确定是否满足题目要求」的数的范围。那些在区间外面的数,都是已确定的满足(不满足)题目要求的数。

:什么是循环不变量?

:想一想,对于求最小的题目,开区间二分的写法,为什么最终返回的是 ,而不是别的数?在初始化(循环之前)、循环中、循环结束后,都时时刻刻保证 check(right) == truecheck(left) == false,这就叫循环不变量。根据循环不变量,循环结束时 ,那么 就是最小的满足要求的数(因为再 就不满足要求了),所以答案是

:部分题目可以优化二分边界,减少二分次数,从而减少代码运行时间。对于初次接触二分答案的同学,无需强求自己写出最优的代码,设定一个比较大的二分上界也是可以的。

开区间二分模板(求最小):

Python3
Java
C++
Go
class Solution:
    # 计算满足 check(x) == True 的最小整数 x
    def binarySearchMin(self, nums: List[int]) -> int:
        # 二分猜答案:判断 mid 是否满足题目要求
        def check(mid: int) -> bool:
            # TODO

        left =   # 循环不变量:check(left) 恒为 False
        right =   # 循环不变量:check(right) 恒为 True
        while left + 1 < right:  # 开区间不为空
            mid = (left + right) // 2
            if check(mid):  # 说明 check(>= mid 的数) 均为 True
                right = mid  # 接下来在 (left, mid) 中二分答案
            else:  # 说明 check(<= mid 的数) 均为 False
                left = mid  # 接下来在 (mid, right) 中二分答案
        # 循环结束后 left+1 = right
        # 此时 check(left) == False 而 check(left+1) == check(right) == True
        # 所以 right 就是最小的满足 check 的值
        return right

思维扩展

§2.2 求最大

一图掌握二分答案!四种写法!

在练习时,请注意「求最小」和「求最大」的二分写法上的区别。

前面的「求最小」和二分查找求「排序数组中某元素的第一个位置」是类似的,按照红蓝染色法,左边是不满足要求的(红色),右边则是满足要求的(蓝色)。

「求最大」的题目则相反,左边是满足要求的(蓝色),右边是不满足要求的(红色)。这会导致二分写法和上面的「求最小」有一些区别。

以开区间二分为例:

  • 求最小:check(mid) == true 时更新 right = mid,反之更新 left = mid,最后返回 right
  • 求最大:check(mid) == true 时更新 left = mid,反之更新 right = mid,最后返回 left

对于开区间写法,简单来说 check(mid) == true 时更新的是谁,最后就返回谁。相比其他二分写法,开区间写法不需要思考加一减一等细节,推荐使用开区间写二分

开区间二分模板(求最大):

Python3
Java
C++
Go
class Solution:
    # 计算满足 check(x) == True 的最大整数 x
    def binarySearchMax(self, nums: List[int]) -> int:
        # 二分猜答案:判断 mid 是否满足题目要求
        def check(mid: int) -> bool:
            # TODO

        left =   # 循环不变量:check(left) 恒为 True
        right =   # 循环不变量:check(right) 恒为 False
        while left + 1 < right:
            mid = (left + right) // 2
            if check(mid):
                left = mid  # 注意这里更新的是 left,和上面的模板反过来
            else:
                right = mid
        # 循环结束后 left+1 = right
        # 此时 check(left) == True 而 check(left+1) == check(right) == False
        # 所以 left 就是最大的满足 check 的值
        return left  # check 更新的是谁,最终就返回谁

§2.3 二分间接值

二分的不是答案,而是一个和答案有关的值(间接值)。

§2.4 最小化最大值

本质是二分答案求最小。二分的 表示上界。

好比用一个盖子(上界)去压住最大值,看看能否压住( 函数)。

§2.5 最大化最小值

本质是二分答案求最大。二分的 表示下界。

§2.6 第 K 小/大

例如数组 ,其中第 小、第 小和第 小的数都是 ,第 小和第 小的数都是

  • 小等价于:求最小,满足 的数至少 个。
  • 大等价于:求最大,满足 的数至少 个。

注 1:一般规定 开始,而不是像数组下标那样从 开始。

注 2:部分题目也可以用堆解决。

三、三分法

四、其他

关联题单

二分答案的一个难点是 check 函数怎么写,这会涉及到贪心等技巧,可以练练下面的贪心题单(主要是第一章节)。

算法题单

如何科学刷题?

  1. 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
  2. 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
  3. 单调栈(基础/矩形面积/贡献法/最小字典序)
  4. 网格图(DFS/BFS/综合应用)
  5. 位运算(基础/性质/拆位/试填/恒等式/思维)
  6. 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
  7. 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
  8. 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
  9. 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
  10. 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
  11. 链表、树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA)
  12. 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)

我的题解精选(已分类)

欢迎关注 B站@灵茶山艾府

如果你发现有题目可以补充进来,欢迎评论反馈。

评论 (162)