
一起动脑筋 · 先看一个小故事
有序卡片是 1、3、5、7、9,女孩要找 7。先看中间的 5,为什么能放心放下左半边?
把过程摊开来看
- 中间是 5比 7 小
- 排除左侧那里都不大于 5
- 留下右侧7、9
- 继续比较找到 7
二分查找利用有序性缩小候选区间。
比较中间值后,可以排除不可能含目标的一侧。每一步都要更新边界,确保保留所有仍有可能的位置。
每次为什么能排除一半?
只在从小到大排列的卡片中找 7。灰色卡片表示已经排除。
13579
中间值 5 小于 7;它和左侧都不可能是目标。
×××79
按向下取整的中间下标,比较下标 3 的值 7。
×××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)?
每次都把剩余范围大约减半:
1024→512→256→ ... →1
1024 个元素大约只需要 10 次“减半”就能缩到 1 个范围。
你已经知道了什么
- 二分查找每次排除大约一半候选。
- 普通数组二分查找要求数据有序。
- 区间边界必须更新正确,否则可能死循环。
- 重复元素时普通二分不保证找到第一个或最后一个。
- 时间复杂度是 O(log n)。
下一篇:冒泡排序:让大的数慢慢浮上来
轮到你来试一试
中间值大于目标时,应该保留更大的右边吗?
想好了吗?点开看解释
应保留左边较小的一侧。中间值本身不等于目标时,也应按正确边界规则排除。