1、二分查找是啥玩意儿?
二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法,前提是数据结构必须先排好序,可以在数据规模的对数时间复杂度内完成查找。
2、啥时候用啊🤔?
先来看一个数组:let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 如果我要找到第六个数5的索引,你是不是想这样找👇
let target = 5;
for(let i = 0, len = arr.length; i < len; i++){
if(arr[i] == target){
return i;
}
}
然后我们分析一下这段代码,假设数组的长度为n,就要找n次,时间复杂度为O(n) 。如果数据量很大呢?100个,1000个,10000个~甚至10万个更多。那时间复杂度是不是就跟着线性往上涨了?这时候二分查找就派上用场了。
3、二分查找咋写啊?
一般思路:因为数据是排好序的,所以我们可以这样做
1、用两个指针,一个指向第一个数,left = 0,一个指向最后一个数,right = arr.length - 1。
2、然后再取中位数(这里中位数的索引用mid表示),判断该中位数 arr[mid] 和目标值target的大小。
3、如果中位数刚好是需要找的那个数,直接返回索引mid,如果大于目标值呢?因为数组有序,肯定在中位数的前面,即0到(mid-1),如果小于目标值呢?肯定在中位数的后面,即 mid+1 到数组的最后一位。
4、执行上面三步,关键是什么时候停止呢?你想一下,每次搜索范围减少一半,当left = right的时候,说明搜索范围已经是一个数了,不能再缩小了,再缩小就可以停止了。
let target = 5;
let left = 0
let right = arr.length - 1;
while(left <= right){
let mid = Math.floor((right + left) / 2)
if(arr[mid] === target){ //找到目标值
return mid;
}else if(arr[mid] > target){ //比目标值大,说明数在前半部分,缩小右边界
right = mid - 1;
}else{ //比目标值小,说明数在后半部分,缩小左边界
left = mid + 1;
}
}
4、二分查找该注意啥呀?
一、关于各种边界条件
let begin = 0;
let end = arr.length-1; //写成这样,相当于搜索区间为[begin, end],这是一个闭区间
while(begin <= end) {//重点: 因为闭区间,所以到了begin等于end时,其实区间内还有一个值要判断,
//因此只有begin>end的时候才能停止
let mid = (begin + end) >>> 1;//位运算,无符号右移一位,同Math.floor((begin+end)/2)
if(arr[mid] == target) {
return mid;
}
else if(arr[mid] > target) {
end = mid - 1;//因为是闭区间,搜索范围变为[left, mid - 1]
}
else if(arr[mid] < target) {
begin = mid + 1; //搜索范围变成[mid + 1, end]
}
}
return -1;
}
或者这样⬇️
let begin = 0;
let end = arr.length; //写成这样,相当于搜索区间为[begin, end),这是一个**前闭后开的区间**
while(begin < end) {//重点:
//因为前闭后开的区间,所以到了begin等于end时,其实区间内已经没有值了,直接停止
let mid = (begin + end) >>> 1;
if(arr[mid] == target) {
return mid;
}
else if(arr[mid] > target) {
end = mid;//因为是闭区间,搜索范围变为[left, mid - 1]
}
else if(arr[mid] < target) {
begin = mid + 1; //搜索范围变成[mid + 1, end]
}
}
return -1;
}
5、实操一下呗
var guessNumber = function(n) {
let left = 1, right = n;
while (left < right) { // 循环直至区间左右端点相同 可以理解为一个开区间
const mid = Math.floor(left + (right - left) / 2);
if (guess(mid) <= 0) {
right = mid; // 答案在区间 [left, mid] 中
} else {
left = mid + 1; // 答案在区间 [mid+1, right] 中
}
}
// 此时有 left == right,区间缩为一个点,即一定为答案
return left;
};
一个小小的说明:本人更多的是按照自己的逻辑整理了一些大佬的文章,尝试帮助大家在清晰的逻辑线上理解某一些问题,但是更主要的内容支持还是参考中所标注的文章。最后希望这小小的整理能对大家有所帮助,一起加油吧家人们~