二分查找 & 二分答案

高效搜索与参数空间探索

二分查找原理 (Binary Search)

二分查找是一种在 **有序数组** 中查找特定元素的高效搜索算法。它通过不断将搜索区间减半来快速定位目标值,时间复杂度为 O(log n)。

目标: 在下面的有序数组中查找数字 42

Low
High

1. 初始化左右指针:low = 0high = n-1

2. 当 low <= high 时循环:

  a. 计算中间索引:mid = low + (high - low) / 2

  b. 比较 array[mid] 与 目标值:

    - 如果 array[mid] == target,找到!返回 mid

    - 如果 array[mid] < target,目标在右侧,更新 low = mid + 1

    - 如果 array[mid] > target,目标在左侧,更新 high = mid - 1

3. 如果循环结束仍未找到,表示目标值不存在,返回 -1。

请点击“下一步”开始演示。

二分答案思路 (Binary Answer)

二分答案是二分思想的一种扩展应用,它并不直接在数据索引上进行二分,而是在 **答案的可能取值范围** 内进行二分搜索。前提是问题具有 **单调性**,即存在一个判定函数 check(x),使得当 x 增大(或减小)时,check(x) 的结果会从 false 变为 true(或反之),且一旦变化就不再变回。

目标: 找到满足某个条件 check(x) 的最小(或最大)的 x

示例场景: 假设我们要找到满足 check(x) == true 的 **最小** x,且答案范围在 [0, 100] 之间。假设 check(x)x >= 60 时为 true,否则为 false

1. 确定答案的可能范围 [l, r]

2. 当 l < r 时循环 (寻找最小值模板):

  a. 计算中间值:mid = l + (r - l) / 2

  b. 调用判定函数 check(mid):

    - 如果 check(mid)true (mid 可能是一个解,尝试更小的),更新 r = mid

    - 如果 check(mid)false (mid 太小了,需要更大的),更新 l = mid + 1

3. 循环结束,l (或 r) 即为所求的最小答案。

注意: 寻找最大值的模板略有不同 (通常是 l = mid, r = mid - 1 配合 mid = l + (r - l + 1) / 2)。

请点击“下一步”开始演示。

应用场景与对比

二分查找应用场景

  • 精确查找: 在有序数组中查找特定元素是否存在及其位置。
  • 边界查找:
    • 查找第一个等于 target 的元素。
    • 查找最后一个等于 target 的元素。
    • 查找第一个大于等于 target 的元素 (类似 C++ `lower_bound`)。
    • 查找第一个大于 target 的元素 (类似 C++ `upper_bound`)。
    • 查找最后一个小于等于 target 的元素。
    • 查找最后一个小于 target 的元素。
  • 部分有序查找: 在旋转排序数组中查找元素 (需要根据 mid 判断哪部分有序)。

二分答案应用场景

核心在于问题答案具有单调性,可以通过 check(mid) 函数判断当前尝试的答案 mid 是否可行,并以此缩小答案范围。

  • 最大化最小值: 如“将 n 个物品分成 k 组,使得每组总和最小的那一组最大化”(二分最小总和)。
  • 最小化最大值: 如“用 k 辆车运输 n 件货物,使得运载量最大的那辆车的载量最小化”(二分最大载量)。
  • 求解特定阈值: 找出满足某个条件的最小/最大阈值。例如,“至少需要多少时间/速度/能力才能完成任务”。
  • 实数域二分: 在浮点数范围内查找满足精度要求的解。

关键: 能否设计出一个 check(potential_answer) 函数,判断该答案是否可行/满足要求,并且这个可行性随 potential_answer 变化是单调的。

二分查找 vs 二分答案

特点 二分查找 二分答案
搜索对象 数据本身 (在有序序列中的位置) 问题的解 (答案的可能值)
搜索空间 数组的索引范围 [0, n-1] 答案的可能取值范围 [min_val, max_val]
核心操作 比较 array[mid]target 调用判定函数 check(mid)
前提条件 数据序列有序 答案的可行性具有单调性
目标 找到目标值的位置或确定不存在 找到满足条件的最小/最大解
时间复杂度 O(log n) O(log(Range) * Tcheck),其中 Tcheck 是判定函数的时间复杂度

选择题与练习

问题1:二分查找算法的时间复杂度是多少?

A. O(n)
B. O(log n)
C. O(n log n)
D. O(1)

总结

二分查找 (Binary Search) 要点

  • 核心思想: 在有序序列中,每次将搜索区间减半。
  • 前提: 数据必须有序(或具有局部有序性)。
  • 实现关键: 正确处理边界条件 (low <= high 还是 low < high) 和区间更新 (mid+1, mid-1, mid)。
  • 复杂度: O(log n)。
  • 应用: 精确查找、边界查找 (第一个/最后一个满足条件)。

二分答案 (Binary Answer) 要点

  • 核心思想: 在答案的可能取值范围内进行二分,通过判定函数缩小范围。
  • 前提: 问题的可行性关于答案值具有单调性。
  • 实现关键: 设计高效的 check(mid) 判定函数,并根据其结果正确更新答案范围 lr。注意不同模板(求最小/最大)的细节差异。
  • 复杂度: O(log(Range) * Tcheck)。
  • 应用: 最大化最小值、最小化最大值、求解阈值问题。

选择关键: 当问题直接要求在有序数据中“查找”时,优先考虑二分查找。当问题要求“求解最优值”(如最大、最小)且该最优值对应的可行性具有单调性时,考虑二分答案。