前言
假设有一个管道长为1000米,有一个未知的地方破了口,想要找出这个口,我们该怎么寻找?
在我们实际运用当中,很多地方都可以看到顺序排放的序列,比如按照价格递增的顺序排放,按照物体的大小进行依次排放,还有就是像上面的管道检查的问题等等…
如果需要查找某一个符合条件的值,依次查找就会花费很多不必要的查找,这时候使用二分就可以节约很大的成本。
面对上面1000米的管道,我们就可以采取二分机制,每次排除竟可能多的管道进行下一步查找。
二分查找
为了更好的理解模板的查找过程,我们先举个简单的例子
在一个有序列表中,查找出54这个元素
第一轮
l = 0
r = 13
mid = (l + r) / 2 = 6
此时 mid 指向的数小于54,所以54在 mid 与 r 之间,可以排除l到 mid 的所有数据。
第二轮
l = mid + 1 = 7
r = 13(不变)
mid = (l + r) / 2 = 10
此时 mid 指向的数大于54,所以54在 l 与 mid 之间,可以排除 mid 到 r 的所有数据。
第三轮
l = 7(不变)
r = mid = 10
mid = (l + r) / 2 = 8
此时 mid 指向的数即是54,查找结束。
时间复杂度:O(logN)
空间复杂度:O(1)
这里的复杂度其实是平均复杂度,算法中的log级别的时间复杂度都是使用了分而治之的思想,这个底数是由分治的复杂度所决定的,也就是说,使用了二分机制查找,那它的底数就是2。
模板1
参照某位大佬的模板,我对次做了一些解释,方便学习
当我们将区间[l, r]
划分成[l, mid]
和[mid + 1, r]
时,其更新操作是r = mid
或者l = mid + 1
;,计算mid时不需要加1。
代码模板
function search_1(l, r)
{
while (l < r)
{
var mid = l + r >> 1;
if(/*mid找到*/) return mid;
if (check(mid)) r = mid;
else l = mid + 1;
}
return false;
}
-
为什么是
l < r
作为结束条件呢?解答:因为根据上面的图,如果还没有找到目标的话,左右指针就会重叠,一旦重叠就要退出了。
-
为什么使用
l + r >> 1
,还有其他方法吗?解答:在JavaScript中,可以通过
parseInt()
和Math.floor()
方法将小数向下取整,而>>
是采用位移的方式,它看起来像个箭头,表示向右移一位,即除于2再取整,这时候就和Math.floor((l + r) / 2)
效果相同。
模板2
当我们将区间[l, r]
划分成[l, mid - 1]
和[mid, r]
时,其更新操作是r = mid - 1
或者l = mid
;,此时为了防止死循环,计算mid
时需要加1。
function search_2(l, r)
{
while (l < r)
{
var mid = l + r + 1 >> 1;
if(/*mid找到*/) return mid;
if (check(mid)) l = mid;
else r = mid - 1;
}
return false;
}
运用
还是上面的数组,使用模板1查找54
var array = [1, 2, 4, 9, 11, 15, 20, 25, 54, 90, 100, 110, 111, 242];
function search_1(nums, target) {
if (target == null || nums.length == 0) return false;
var l = 0,
r = nums.length - 1;
while (l < r) {
var mid = (l + r) >> 1;
if(target == nums[mid]) return mid;
if (target < nums[mid]) {
r = mid;
} else {
l = mid + 1;
}
}
return false;
}
function search_2(nums, target) {
if (target == null || nums.length == 0) return false;
var l = 0,
r = nums.length - 1;
while (l < r) {
var mid = (l + r + 1) >> 1;
if(target == nums[mid]) return mid;
if(target > nums[mid]) l = mid;
else r = mid - 1;
}
return false;
}
总结
二分查找要求数据是有序的。
- 中间位置与目标比较,相等则找到。否则,将原表拆分成两个表
- 当
l > r
时,则未找到。