二分查找的概念本身很容易理解,这里不做赘述。其使用前提只有一个:单调数组
朴素二分查找
当在一个有向数组中,查找数组中是否有某个值,代码很简单:
const binarySearch = (nums, target) => {
let l = 0;
let r = nums.length - 1;
while(l<=r) {
const mid = (l+r) >> 1
if(nums[mid]<target) {
l = mid + 1
} else if(nums[mid]>target) {
r = mid - 1
} else {
return mid
}
}
return -1
};
各类变种题及解题模板
实际使用时,用到朴素二分查找的情况反而更少,例如将4
插入数组[1,2,2,3,3,4,4,5]
中,插入后的数组保持单调递增,我们需要先找到小于4的边界(3的索引),再将4插入到这个边界后面。
如何理解这个查找过程
首先将数组看作如图所示红蓝两个区域,红区代表所有小于target
的值,蓝区代表所有不小于target
的值。那么应该如何找到红区和蓝区的边界?
解题模板
模板如下:
// 通用模板
const binarySearch = (arr, isLeftPart) => {
// 边界处理
if(!isLeftPart(arr[0])) return [-1, 0];
if(isLeftPart(arr[arr.length - 1])) return [arr.length-1, arr.length];
let l = 0;
let r = arr.length - 1;
// 注意,条件是l<r-1
while(l<r-1) {
const mid = (l+r)>>1;
// 说明mid处于红区,需要给l赋值mid
if(isLeftPart(arr[mid])) {
l = mid
} else {
r = mid
}
}
return [l, r];
}
// 测试代码
const arr = [1,2,2,3,3,4,4,5];
const [_, idx] = binarySearch(arr, (val)=>val<4);
// insert
arr.splice(idx, 0, 4);
console.log(arr)
通用模板返回结构[l, r]
,l
代表红区的右边界,r
代表蓝区的左边界。
无论如何变化,这类变种题最后要找的一般就是l
或者r
。
while
的边界问题
朴素二分查找中,while
条件是while(l<=r)
,而解题模板中则是while(l<r-1)
,这种边界是如何区分的?
我们需要明白,while代表终止条件,第一个不满足条件的情况即为终止时的状态。
以通用模板为例,终止时l
和r
的指向应如图所示:
可以看到,这个终止态时,l === r-1
,那么非终止态,也就是while
循环条件应当是while(l<r-1)
.
同理,对于朴素二分查找,l===r
时,仍要判断该值是否是查找值,只有当l>r
才能断定查找完毕,因此while
条件是while(l<=r)
。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。