路人甲:你说,@戚洪昊是不是不玩了?
路人乙:那哪能啊!
路人甲:那他咋不发博客啦?
路人乙:你看,他现在不就发了么...
路人甲:阿巴阿巴……
好吧,我承认上边的东西纯属凑字数
但是我雀食好久没有发博客了
今天给大家带来二分查找的故事~
大家好,我叫二分查找。
听说我干事的效率很快,所以被作者揪出来了。
这么说吧,我可能不算太简单
但是肯定不能把你难倒
先来简要介绍一下我的工作原理吧
使用我这种算法
首先需要定义两个变量——
左边(l)和右边(r)!
自然,还少不了一个变量m(中间)
m的算法为:(l+r)/2
很好的一个公式
看不懂?我来解释一下
现在,有一个数组a[5](排过序的)
1 3 4 6 7
现在要在a数组中找到3 并且输出下标
这里有两种办法:
方法1:朴素枚举(时间复杂度:O(n))
for(int i=0;i<n;i++)
if(a[i]==key) cout<<i;
方法2:二分查找(时间复杂度:O(log2N))
int binarySearch(int a[], int N, int aa) {
bool found=false;
int l=0,r=N,m=0;
while(l<r) {
m=(l+r)/2;
if(a[m] == aa) {
found=true;
break;
}
if(a[m] > aa) {
r=m;
} else if(a[m] < aa) {
l=m+1;
}
}
if(found)
return (m+1);
else
return -1;
}
由于枚举递增的特性
导致暴力枚举的时间复杂度一定是O(n)
但是我们可以想办法优化
还记得小时候玩的猜数字游戏吗?
甲:我想一个数,你来猜大小
乙:好啊好啊!
甲:我想好了,你猜吧
乙:50?
甲:大了!
乙:25?
甲:小了!
乙:37?
甲:小了!
乙:43?
甲:大了!
乙:40?
甲:小了!
乙:41?
甲:对了!
乙:哈哈哈!
上面就是一个猜数字游戏的标准玩法
可以看到乙猜数字都是对半ka的
这样可以使猜数字的次数最少
二分就运用了这个原理
想到这儿
你是不是顿悟了?
二分的基本原理就是——
大了ka一半,小了ka一半
首先,咱们先来写一个框架:
int l=0,r=N,m=0;
while(l<r){
...
}
现在,咱们来给这个只有躯壳的while循环添加灵魂
(阿巴阿巴阿巴[添加灵魂的声音])
好了,现在咱们再来看这个二分循环:
int l=0,r=N,m=0;
while(l<r) {
m=(l+r)/2;
if(a[m] == aa) {
found=true;
break;
}
if(a[m] > aa) {
r=m;
} else if(a[m] < aa) {
l=m+1;
}
}
在这个循环内,首先Middle(m)为l+r的一半 就是(l+r)/2
这就是我们刚刚说的ka~
当然,如果ka完以后Middle下标对应的数就是原数的话就直接打破循环
如果不是的话,你就可以问问数组:
大了还是小了?
如果数组告诉你 大了
那么left(l)就是m+1了
这样,这个数组的大小就变成了从m+1到r
那么数组小了的话
数组的右边界(r)就变成了m
然后就再次循环~
再算一个m
再进行判断~
于是,我们就可以得到他的在函数中的完整代码:
int binarySearch(int a[], int N, int aa) {
bool found=false;
int l=0,r=N,m=0;
while(l<r) {
m=(l+r)/2;
if(a[m] == aa) {
found=true;
break;
}
if(a[m] > aa) {
r=m;
} else if(a[m] < aa) {
l=m+1;
}
}
if(found)
return (m+1);
else
return -1;
}
在这个函数中 a代表传入的数组 N表示数组的大小 aa(随便取得)表示要查找的值
于是,我们就得到了这个算法的概念!
二分查找,顾名思义,就是100分的测试点只能得2分的查找
好吧,其实是
二分查找又称折半查找,优点是比较次数少,查找速度快,平均性能好,占用系统内存较少;其缺点是要求待查表为有序表,且插入删除困难。因此,折半查找方法适用于不经常变动而查找频繁的有序列表。首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;否则利用中间位置将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。 ——360百科
那我们来试几道题练练手吧
好啦,二分查找就讲到这,大家后会有期~
(有没有期还真不知道)