章节概述

二分查找在有序序列中每次将查找范围减半,将查找复杂度从 O(n) 降至 O(log n)。
是算法竞赛中最基础也最重要的优化手段之一。

核心原理

1. 数学基础

在有序数组 a[1..n] 中查找目标值 x,每次取中点 mid = (l+r)/2:

  • a[mid] == x:找到目标
  • a[mid] < x:目标在右半,l = mid + 1
  • a[mid] > x:目标在左半,r = mid - 1

每步将搜索空间减半,时间复杂度:

对 100 万元素,最多比较约 20 次。

2. 两种终止条件

写法条件结束状态适用场景
while(l <= r)l > r 时退出l = r+1精确查找
while(l < r)l == r 时退出l = r查找边界(lower_bound)

3. 三个核心变体

变体含义实现
lower_bound第一个 ≥ x 的位置a[mid] >= xr = mid - 1
upper_bound第一个 > x 的位置a[mid] > xr = mid - 1
binary_search是否存在 x标准二分查找到即返回

4. 防溢出技巧

int mid = l + (r - l) / 2;  // 安全,不会溢出
// 而非 mid = (l + r) / 2;  // l+r 可能溢出

关键数据结构


P2249 查找

题目: 输入 n 个单调不减的非负整数,然后 q 次询问,每次询问数字 x 第一次出现的位置(从 1 开始编号)。若不存在则输出 -1。

思路: 使用 lower_bound 二分查找第一个 ≥ x 的位置,再判断是否等于 x。

#include <iostream>
using namespace std;
int a[1000005];
 
int main() {
    int n, q;
    cin >> n >> q;
    for (int i = 1; i <= n; i++) cin >> a[i];
    while (q--) {
        int x;
        cin >> x;
        int l = 1, r = n, ans = -1;
        while (l <= r) {
            int mid = (l + r) / 2;
            if (a[mid] >= x) {
                ans = mid;
                r = mid - 1;
            } else {
                l = mid + 1;
            }
        }
        if (ans != -1 && a[ans] == x)
            cout << ans << " ";
        else
            cout << -1 << " ";
    }
    return 0;
}

P1102 A-B 数对

题目: 给出一串正整数数列以及一个正整数 C,要求计算出所有满足 A-B=C 的数对 (A,B) 的个数。
不同位置的数字一样属于不同的数对。

思路: 对于每个 A,求 B=A-C 在数组中的出现次数。用二分查找找到 B 的左右边界。

#include <iostream>
#include <algorithm>
using namespace std;
int a[200005];
 
int main() {
    int n, c;
    long long ans = 0;
    cin >> n >> c;
    for (int i = 0; i < n; i++) cin >> a[i];
    sort(a, a + n);
    for (int i = 0; i < n; i++) {
        int target = a[i] - c;
        int l = lower_bound(a, a + n, target) - a;
        int r = upper_bound(a, a + n, target) - a;
        ans += r - l;
    }
    cout << ans << endl;
    return 0;
}

P1678 烦恼的高考志愿

题目: 有 m 所学校每所预计分数线为 b_j,有 n 个学生每个考分为 a_i。每个学生的”不满意度”为其分数与
所选学校分数线的差的绝对值的最小值。求所有学生不满意度之和的最小值。

思路: 将学校分数线排序,对每个学生二分查找最接近的分数线,计算差值。

#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
int b[100005];
 
int main() {
    int m, n;
    long long ans = 0;
    cin >> m >> n;
    for (int i = 0; i < m; i++) cin >> b[i];
    sort(b, b + m);
    for (int i = 0, x; i < n; i++) {
        cin >> x;
        int pos = lower_bound(b, b + m, x) - b;
        int diff = 2e9;
        if (pos < m) diff = min(diff, abs(b[pos] - x));
        if (pos > 0) diff = min(diff, abs(b[pos - 1] - x));
        ans += diff;
    }
    cout << ans << endl;
    return 0;
}

推荐练习题(洛谷)


相关技巧


多平台练习

| 洛谷 | 本题单 | 竞赛基础 |
| POJ (北大) | PKU JudgeOnline | 经典题目,适合巩固 |
| HDU (杭电) | HDU OJ | 暑期多校训练 |
| Codeforces | Codeforces | 国际竞赛,适合提升 |