什么是"二分查找"?一句话
就是每次从中间砍一半,看看目标在左边还是右边,一步步缩小范围,直到找到或者没找到。
看起来高效又帅气,但它不是万能的。它有几个硬性要求,满足了才有用。
前提一:必须是排好序的数据
这是第一大铁律。不排序?别用。
举个例子:
你去图书馆找《西游记》:
- 如果书按书名从A到Z排好,你翻到中间一看:"射雕英雄传",比"西游记"靠前,继续翻后半部分,很快找到了。
- 如果图书馆管理员摆书像堆衣服一样乱,那就别想二分查找了,只能一摞一摞翻。
再比如:
你有一堆苹果,每个上面写着价格,你想快速找到"价格是5元的苹果"在哪。
- 如果这些苹果已经按价格从低到高排好了,二分查找就派上用场;
- 如果是杂乱地堆一堆,你只能一个个看。
所以,二分查找就是为有序数据准备的VIP通道,乱的别挤。
前提二:得能比大小
说白了,你要能判断目标值和中间值之间的"关系":到底是大了?小了?还是刚刚好?
举个例子:
你猜同学心里想的数字(1~100):
- 你每次猜一个,他说"太小"或"太大";
- 你就能往正确方向收缩;
- 这就能用二分查找。
但如果你在猜"今天班里谁最开心",这个就很尴尬了:
- 你问一个人:"你是不是最开心的?"
- 他回答"也许吧"
- 然后你问下一个,也说"也许吧"......
你根本没法比较大小,那就没法二分。
前提三:数据结构支持随机访问中间
啥意思?你要能很快拿到中间那一项,不能一点点走过去。
举个例子:
- 如果你手上是一摞纸质的小说书,你能一翻就翻到中间那一章。
- 但如果你看的是那种一页页滑的手机小说 App,翻中间要滑很久,那效率就低了。
或者说:
- 数组(Array)支持按下标直接拿,比如
arr[mid]
; - 链表(LinkedList)就不行,得从头往后走,慢得很,不适合二分查找。
前提四:最好是查"具体值"
你要查的目标,是"有没有42""在哪个位置""第一次出现在哪"等明确的问题。
如果你问的是:
- "大于42的数有哪些?"
- "哪个数最接近目标?"
这些模糊问题不是不能用二分,但就需要用变种的版本,得多写几行代码,逻辑也会稍微复杂点。
简单小结一下:
适合用二分查找的场景:
场景 | 为什么可以用? |
---|---|
查字典里的单词 | 单词是按字母排序的,可以比大小 |
猜一个 1~100 的数字 | 数字有序,能说"太大/太小" |
找排好序的快递编号 | 快递单号升序排序,可以比 |
找超市价格最低的商品(排好序) | 有序+能比较+能跳到中间 |
网上查一个按时间排序的日志 | 日志时间排序,可以比大小 |