女孩和机器人一起思考

一起动脑筋 · 先看一个小故事

有序卡片是 1、3、5、7、9,女孩要找 7。先看中间的 5,为什么能放心放下左半边?

把过程摊开来看

沿着编号看一遍,再用自己的话讲一遍
  1. 中间是 5比 7 小
  2. 排除左侧那里都不大于 5
  3. 留下右侧7、9
  4. 继续比较找到 7

二分查找利用有序性缩小候选区间。

比较中间值后,可以排除不可能含目标的一侧。每一步都要更新边界,确保保留所有仍有可能的位置。

每次为什么能排除一半?

只在从小到大排列的卡片中找 7。灰色卡片表示已经排除。

第 1 步 · 看中间下标 2
13579

中间值 5 小于 7;它和左侧都不可能是目标。

第 2 步 · 留下右边两个
×××79

按向下取整的中间下标,比较下标 3 的值 7。

第 3 步 · 找到目标
×××7 ✓9

返回下标 3,即第四张卡;9 不必再检查。

01二分查找为什么快?

如果数据已经按从小到大排列:

2  5  8  12  19  25  31  40

寻找 25 时,不需要从 2 开始一个一个看,可以先看中间位置。

02一次比较怎样排除一半?

看中间值比较 target只保留可能的一半

因为数组有序:如果目标比中间值大,那么中间值左边更小的部分都不可能是目标。

03基本代码怎样写?

int left = 0;
int right = n - 1;

while (left <= right) {
    int mid = left + (right - left) / 2;

    if (a[mid] == target) {
        return mid;
    } else if (a[mid] < target) {
        left = mid + 1;
    } else {
        right = mid - 1;
    }
}

return -1;

04为什么 mid 不直接写 (left + right) / 2?

数学上看起来一样,但 left + right 在某些整数范围下可能先溢出。

left + (right - left) / 2

是常见的更稳妥写法。

05二分查找必须满足什么条件?

这里这种二分查找必须依赖数据有序。

06有重复元素时会找到哪一个?

普通“找到就返回”的二分查找,只保证找到某个匹配位置,不保证一定是第一个或最后一个。

如果题目要求“第一个 ≥ x”或“最后一个 ≤ x”,需要设计对应的边界二分。

07为什么是 O(log n)?

每次都把剩余范围大约减半:

1024512256→ ... →1

1024 个元素大约只需要 10 次“减半”就能缩到 1 个范围。

你已经知道了什么

  • 二分查找每次排除大约一半候选。
  • 普通数组二分查找要求数据有序。
  • 区间边界必须更新正确,否则可能死循环。
  • 重复元素时普通二分不保证找到第一个或最后一个。
  • 时间复杂度是 O(log n)。

下一篇:冒泡排序:让大的数慢慢浮上来

轮到你来试一试

中间值大于目标时,应该保留更大的右边吗?

想好了吗?点开看解释

应保留左边较小的一侧。中间值本身不等于目标时,也应按正确边界规则排除。