什么是二分查找
代码始终在维护着有序A数组的一个子序列[left, right]
,我们的target可能就在这个区间。
我们不断的折半缩小子序列[left, right]
,从而找到target。
关键步骤
- 序列必须有序,这个就不提了,简单选择排序,冒泡排序,快速排序都是非常常见的处理方法。
- 初始left = 0、right = length-1。
- 在
left < right
的前提下。
二分的关键,比较点位mid为left + (right-left)/2
向下取整。
这里不写(left+right)/ 2
,是为了避免整数溢出,细节嘛,注意一下。
向下取整也会造成问题,剩下两个元素时,会漏掉最右的元素(0 + 1 )/ 2 = 0
, 所以right = mid。
同理,向上取整,剩下两个元素时,会漏掉最左的元素(0 + 1)/ 2 = 1
。这里以向下取整为例。 - 在
left < right
的前提下。
如果A[ mid ] < target
那就加大left,left = mid + 1
,缩小[left, right]
区间,回到3。
但是,如果left == right
,退出循环。 - 在
left < right
的前提下。
如果A[ mid ] > target
那就减小right,right = mid - 1
,缩小[left, right]
区间,回到3。
但是,如果left == right
,退出循环。 - 在
left < right
的前提下。
如果A[ mid ] == target
,返回结果,找到target,退出循环。 - 此时循环退出,
left == right
。但此时A[ left]
是否等于target
并没有做判断。
判断if(newArray[left] == target) return true;
。这里同时也处理了A为单元素数组的特殊情况A=[-5]
初级代码
const target = -5;
const array = [ -5, 6, 8 ]
console.log(binarySearch(array, target)); // true
function binarySearch(newArray, target) {
let left = 0, right = newArray.length - 1;
while(left < right){
let mid = Math.floor(left + (right-left)/2);
if(newArray[mid] > target) {
right = mid;
} else if(newArray[mid] < target){
left = mid + 1;
}else{
return true;
}
}
return false;
}
二分查找的优化
target 不在A序列中
因为A是有序序列,在二分查找前可以首先判别,如果target 不在A序列中,直接返回false。
if(newArray[left] > target || newArray[right] < target) {
return false;
}
最终代码
function binarySearch(array, target) {
let left = 0, right = array.length - 1;
if(array[left] > target || array[right] < target) {
return false;
}
while(left < right){
let mid = Math.floor(left + (right-left)/2);
if(array[mid] > target) {
right = mid;
} else if(array[mid] < target){
left = mid + 1;
}else{
// left < right时, array[left] == target
return true;
}
}
// left == right时, 判断array[left] == target
if(array[left] == target) return true;
return false;
}
易错点总结
- 更新mid时,避免整数溢出。
mid = Math.floor(left + (right - left) / 2)
。 - 向下取整,当
Amid] > target
时,更新right = mid
,以免忽略最右元素。 - 循环结束的条件是
left == right
,但循环结束时A[left]
是否等于target还未做判断。
递归二分查找
function binarySearch_recursion(array, target) {
let left = 0, right = array.length - 1;
if(array[left] > target || array[right] < target) {
return false;
}
return recursion(array, target);
function recursion(array, target) {
let left = 0, right = array.length - 1;
let mid = left + Math.floor((right - left) / 2);
if(left < right) {
if(array[mid] < target){
return recursion(array.slice(mid + 1, right + 1), target);
} else if(array[mid] > target) {
return recursion(array.slice(left, mid + 1), target);
} else {
return true;
}
} else if(left == right) {
if(array[left] == target) return true;
else return false;
} else {
return false;
}
}
}