故事分享:
有一天小明到图书馆借了 N 本书,出图书馆的时候,警报响了,于是保安把小明拦下,要检查一下哪本书没有登记出借。小明正准备把每一本书在报警器下过一下,以找出引发警报的书,但是保安露出不屑的眼神:你连二分查找都不会吗?于是保安把书分成两堆,>让第一堆过一下报警器,报警器响;于是再把这堆书分成两堆…… 最终,检测了 logN 次之后,保安成功的找到了那本引起警报的书,露出了得意和嘲讽的笑容。于是小明背着剩下的书走了。 从此,图书馆丢了 N - 1 本书。
保安怎么知道只有一本书没有登记出借,万一全部都没有登记呢?
这个故事其实说出了二分查找需要的条件
用于查找的内容逻辑上来说是需要有序的
查找的数量只能是一个,而不是多个
比如在一个有序的数组并且无重复元素的数组中,例如[1, 2, 3, 4, 5, 6],需要查找3的位置就可以使用二分查找。
在二分查找中,目标元素的查找区间的定义十分重要,不同的区间的定义写法不一样
二分法的思想很简单,因为整个数组是有序的,数组默认是递增的。
二分法就是按照这种方式进行快速排除查找的
不用去纠结数组的长度是奇数或者偶数的时候,怎么取长度的一半,以下是说明,可以跳过。
是奇数的情况很简单,指向中间的数字很容易理解,如果需要查找的数字为29
因为29大于中间的数字大于11,所以左边的所有数字全部排除
这个时候中间的数字两边的数字数量就不一样了,如果长度为偶数就为上图中间的数字两边的相差为 1
两边数量不一样是一定会出现的情况
但是这种情况并不影响我们对中间数字和目标数字大小关系的判断
只要中间数字大于目标数字,就排除右边的
只要中间数字小于目标数字,就排除左边的
有N个元素的数组,前K个元素区域为蓝色,后N-K个为红色。然而在这个问题中,蓝红边界的位置是未知的,即K是未知的。
在我们拿到数组的时候,整个数组默认都是灰色区域,未知的。
这个问题,是把蓝红边界找出来。
下面的蓝色就是满足查找条件的判断边界。
将数组左侧的蓝色指针,持续不断的向目标区域扩展,
同理,红色指针 不断向左移动,红色区域不断被拓展
加快这一过程,直接看中间值,他的颜色为蓝色
就意味着,他之前的颜色都是蓝色
直接把蓝色指针移动到 中间位置4上
剩下的中间值根据中间值,查看颜色
一开始定义蓝红指针在数组两侧
然后通过二分,将蓝红指针迅速向目标值靠近
#include
using namespace std;
int n, val;
int q[] = {1,2,3,4,5};
int binary_search(int q[], int val)
{
int left = -1;
int right = n;
while (left + 1 != right)
{
int mid = left + right >> 1;
if (q[mid] <= val) left = mid;
else right = mid;
}
if(q[left] == val) return left;
else return -1;
}
int main(){
cin >> n >> val;
int res = binary_search(q, val);
cout << res;
return 0;
}
def binary_serach(li, val):
left = -1
right = len(li)
while left + 1 != right:
mid = (left + right) // 2
if li[mid] <= val:
left = mid
else:
right = mid
if li[left] == val:
return left
else:
return -1
# 有序序列,且无重复值
li = [i for i in range(5)]
index = binary_serach(li, 5)
print(index)
这四个需求,表面上是相似的,但是答案却不同
可见,如果用二分查找来处理这个问题,边界问题处理不好,一不小心就会出错。
这题第一个问题代码示例,后面更改IsBlue判断条件和 返回值r或者l
IsBlue 条件全部写成是 小于等于 待查找值
看题目要求,IsBlue 条件 和题目 小于或者小于等于时 同步 并且返回 l
题目是 大于 或者 大于等于时,IsBlue 条件 取反 小于等于或者小于时 同步 并且返回 r
#include
using namespace std;
int n = 8;
int q[] = {1,2,3,5,5,5,8,9};
int binary_search(int q[], int val)
{
int left = -1;
int right = n;
while (left + 1 != right)
{
int mid = left + right >> 1;
if (q[mid] < val) left = mid;
else right = mid;
}
if(right > n - 1) return -1;
else return right;
}
int main(){
int val;
cin >> val;
int res = binary_search(q, val);
cout << res;
return 0;
}
def binary_serach_1(li, val):
"""
找到第一个">=5"的元素
"""
l = -1
r = len(li)
while l + 1 != r:
mid = (l + r) // 2
if li[mid] < val:
l = mid
else:
r = mid
if r > len(li) - 1:
return -1
else:
return r
求平方根,首先答案在 0–输入值x 之间
|0------------------x|
答案在这之间
#include
using namespace std;
int main(){
double x;
cin >> x;
double l = 0, r = x;
while(r - l > 1e-6)
{
double mid = (l + r) / 2;
if(mid * mid >= x) r = mid;
else l = mid;
}
printf("%lf\n", l);
return 0;
}
def binary_float_serach(val):
"""求平方根"""
l, r = 0, val
while r - l > 1e-6:
mid = (l + r) / 2
# 说明答案在mid的左边
if mid * mid >= val:
r = mid
else:
l = mid
return l