20211204 二分查找

155 阅读2分钟

什么是二分查找

二分查找其实就是一个搜索范围不断收窄的过程。当然,这个搜索过程有它的前提:

  1. 被搜索的集合是有序的
  2. 搜索的内容应该是唯一的,或搜索的结果与搜索关键词是一一对应的(当然,如果集合中有多个搜索关键词,但集合有序,而且不要求返回结果唯一,其实也可以使用二分查找)

那么整个代码的关键点其实就很显而易见了:不断更新搜索范围,并确保搜索范围是有效的。围绕这一点去编写代码即可。

先说说更新搜索范围。首先我们要确定最大的搜索范围——整个集合(通常是一个列表或数组),然后找到这个范围的中点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
    }
}