原理
二分法查找的原理基于有序数组的特性。
它通过不断将数组分割成两部分,反复比较中间元素与目标值的大小关系,从而逐步缩小查找范围。
具体来说:
- 确定数组的左右边界,一般设左边界为 0,右边界为数组长度减 1。
- 计算中间索引 mid,将中间元素与目标值进行比较。
- 如果中间元素等于目标值,直接返回中间索引。
- 如果中间元素小于目标值,则在右半部分继续查找,更新左边界为 mid + 1。
- 如果中间元素大于目标值,则在左半部分继续查找,更新右边界为 mid - 1。
- 重复步骤 2 至步骤 5,直到找到目标值或确定目标值不存在。
这种方法的时间复杂度为 O(log n),相比于顺序查找,效率更高。它的优势在于通过不断缩小查找范围,快速定位目标元素。
LeetCode704实操
这是二分查找一道最经典的案例。
题目
给定一个 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
解题
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var search = function(nums, target) {
var left = 0;
var right = nums.length-1;
while(left <= right){
var mid = Math.floor(left + (right - left)/2);
if(nums[mid] < target){
left = mid + 1;
}else if(nums[mid] > target){
right = mid - 1;
}else {
return mid;
}
}
return -1;
};
需要注意的点
- js不会和java一样自动取整,这里需要我们手动向下取整
- mid值正常写法:(左边加右边)除以2,但是如果值非常大,可能会存在溢出的情况,因此改写为左边+(右边-左边)/2,防止溢出,通分后和正常写法是一样的
- 事实上二分法有两种写法,很多小白朋友不知道要不要加等号,事实上我们只要保持一致性原则就不会出错,当我们while中写的是左边小于等于右边(左闭右闭),那么在我们拿目标值与中间值比较时,中间值已经小于或者大于目标值了,那么接下来的区间就不应该包含这个中间值,就需要+1/-1,而如果我们while中的写法是只小于,没有包含等于的情况(左闭右开),那么当目标值小于中间值时,目标区域在左边,更新右边界,right应该等于mid,因为原来就没有把这个值算在里面,而目标值大于中间值时,目标查询区域在右边,更新左边界,依旧是左边=mid+1,(左闭右开),因此我们时刻保持一致性原则就不会出错。