二分查找
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果 target 存在返回下标,否则返回 -1。
你必须编写一个具有 O(log n) 时间复杂度的算法。
示例 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
提示:
- 你可以假设
nums中的所有元素是不重复的。 n将在[1, 10000]之间。nums的每个元素都将在 [-9999, 9999]之间。
思路
在题目描述中有两个条件很重要:
- 升序整型数组
- 元素不重复
这两个条件是使用二分法的前提条件。
数组示例,如下图所示:
二分法是根据中间元素和目标元素的比较来确定目标元素位于哪个区间。所以需要准备三个变量:
- left 目标区间开始下标
- right 目标区间结束下标
- mid 中间元素下标
如下图:
对于 mid 元素的处理,需要确定一个点,即 mid 元素是否参与目标区间的计算。
根据上图所示,当目标元素值为 9 时,新的目标区间的 left 应该指向当前 mid 元素下标还是当前 mid 元素下标加一。这里有两种不同的处理方式。
如下图:
第一种情况
在第一种情况中,由于 mid 元素未参与新的区间的比较,所以新区间所有元素都是未比较的,这时候需要处理 left 元素等于 right 元素的情况。目标区间为 [left, rght]
var search = function(nums, target) {
let mid
let left = 0
let right = nums.length - 1
while(left <= right) {
mid = left + ((right - left) >> 1)
if(nums[mid] === target) {
// 相等直接返回
return mid
} else if(nums[mid] < target) {
// 中间元素小于目标元素
// 则目标元素在中间元素右边
// 重置 left
left = mid + 1
} else {
// 中间元素大于目标元素
// 则目标元素在中间元素左边
// 重置 right
right = mid - 1
}
}
return -1
};
第二种情况
在第二种情况中,目标区间最右边的元素永远都是无效下标,所以不用处理。目标区间为 [left, rght)
var search = function(nums, target) {
let mid
let left = 0
let right = nums.length
while(left < right) {
mid = left + ((right - left) >> 1)
if(nums[mid] === target) {
// 相等直接返回
return mid
} else if(nums[mid] < target) {
// 中间元素小于目标元素
// 则目标元素在中间元素右边
// 重置 left
left = mid + 1
} else {
// 中间元素大于目标元素
// 则目标元素在中间元素左边
// 重置 right
right = mid
}
}
return -1
};