二分法是一种非常基础且通用的数值计算方法,主要用于求解方程的根(即找到使函数值为 0 的点)。
核心原理:夹逼定理
二分法的理论基础是连续函数的介值定理(Intermediate Value Theorem)。
简单来说:
如果一个函数 f(x) 在区间 [a,b] 上是连续的,并且 f(a) 和 f(b) 的符号相反(即一正一负,意味着图像穿过了 x 轴),那么在这个区间内部至少存在一个点 c,使得 f(c)=0。
此时,我们只需要不断的找中心点,通过比较c在中心点左还是右来减小区间,最终确定c的具体位置。
从数学角度来讲,由于这个方法的鲁棒性强,因此我们可以把它当作解决求根的最后一个方案。
二分查找
核心思想:在有序的数组里,每次砍掉一半不可能的区间,快速找到目标。
前提:数组必须有序。
步骤:
- 设左边界
left,右边界right - 取中点
mid = (left + right) / 2 - 比较
a[mid]和目标值:- 等于 → 找到了
- 小于 → 答案在右边,
left = mid + 1 - 大于 → 答案在左边,
right = mid - 1
- 重复直到找到或区间为空
时间复杂度:O(log n)
100 万的数据最多查 20 次。
应用场景
- 找某个数是否存在
- 找第一个 ≥ x 的位置(lower_bound)
- 找最后一个 ≤ x 的位置
二分答案
核心思想:答案本身具有单调性,先二分答案的值,再通过函数判断该值是否正确。
步骤:
- 最大的最小 xxx
- 最小的最大 xxx
- 满足条件的最小值
- 满足条件的最大值
- 分割数组、最小最大距离、最小花费等
应用场景
- 先确定题目要求的内容
- 根据求的内容确定答案的区间[L,R]
- 根据当前区间,二分出答案mid
- 写一个check(mid)函数:
- 返回true:尝试更优答案
- 返回false:调整方向
- 通过不断缩小区间最后确定最优解
代码相关
确定答案范围:L和R
初始化ans:-1、L或R
初始化mid:作为区间中心点
while (L <= R) {
mid = L + (R - L) / 2;
if (check(mid)) { // 满足条件
ans = mid;
R = mid - 1; // 尝试更小
}
else {
L = mid + 1; // 不够,要更大
}
}
算法库相关
查找value是否存在
- bool binary_search(first, last, value);
查找第一个大于等于value的位置
- iterator lower_bound(first, last, value);
查找第一个大于value的位置
- iterator upper_bound(first, last, value);
同时返回lower和upper
- pair<iterator, iterator> equal_range(…);
- 常用于求value出现的次数