什么是二分查找
二分查找其实就是一个搜索范围不断收窄的过程。当然,这个搜索过程有它的前提:
- 被搜索的集合是有序的
- 搜索的内容应该是唯一的,或搜索的结果与搜索关键词是一一对应的(当然,如果集合中有多个搜索关键词,但集合有序,而且不要求返回结果唯一,其实也可以使用二分查找)
那么整个代码的关键点其实就很显而易见了:不断更新搜索范围,并确保搜索范围是有效的。围绕这一点去编写代码即可。
先说说更新搜索范围。首先我们要确定最大的搜索范围——整个集合(通常是一个列表或数组),然后找到这个范围的中点m=(start+end)/2,去判断这个中点位置的值和我们的目标值,也就是搜索关键词之间的关系。下面以升序集合(即从集合头部到集合尾部,值不断变大)为例。
- 如果目标值(搜索关键词)大于中点值,说明下一步的搜索范围是在当前中点之后到搜索范围尾之间
(m, end]; - 如果目标值(搜索关键词)小于中点值,说明下一步的搜索范围是在当前搜索范围头到中点之前
[start, m); - 如果目标值(搜索关键词)等于中点值,说明……说明成了!
👆这就是每一次查找的时候用来判断的条件了。
那么,如何确保搜索范围是有效的呢?这里就需要再利用搜索范围来结束搜索了。我们可以让搜索范围差值为1且没有找到我们的搜索关键词时结束搜索。
来一道简单的例题吧!
来源:力扣(LeetCode)-704. 二分查找
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
示例 1:
输入:nums = [-1,0,3,5,9,12], target = 9
输出:4
解释:9出现在nums中并且下标为4
示例 2:
输入:nums = [-1,0,3,5,9,12], target = 2
输出:-1
解释:2不存在nums中因此返回-1
代码如下
class Solution {
fun search(nums: IntArray, target: Int): Int {
var start: Int = 0
var end: Int = nums.size - 1
while (start <= end) {
val m: Int = start + (end - start) / 2
val cur = nums[m]
if (cur == target) {
return m
} else if (target > cur) {
start = m + 1
} else {
end = m - 1
}
}
return -1
}
}