【阅读部分】二分查找
在一个有序的数据序列里,如果要查找一个数,普通的办法是从头到尾扫描一遍,但是这样的时间复杂度是O(n)级别。如果有m次查询,那么时间复杂度就达到了O(mn)级别,数据大就超时了。
下面介绍一种O(lg(n))的查找方法,那就是二分查找。
二分查找又称折半查找,优点是比较次数少,查找速度快,平均性能好;其缺点是要求待查表为有序表,且插入删除困难。因此,折半查找方法适用于不经常变动而查找频繁的有序列表。
步骤:首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。
二分查找,只要查找log2(n)次,即可找出答案。而log2(1000000)≈20,速度实在是高效得逆天。
折半查找有递归写法和非递归写法。
1、递归写法:
int bin_search(int key[],int low, int high,int k) //注意:high的值是数组的最后一个单元位置,不是数组元素个数
//key 源数据数组,假设已从小到大排序 。low:查找区域的前端点;high:查找区域的后端点;k:要查找的值
{
int mid;
if(low>high) //如果数组里没有要查找的数据,返回 -1
return -1;
else{
mid =low+ (high-low) / 2; //先计算查找区域的中点位置。也有写成:mid = (low+high) / 2; 但如果low和high很大会有溢出int范围的风险
if(key[mid]==k) //如果mid位置的值就是要查找的,就把mid的值返回
return mid;
if(k>key[mid]) //如果要找的值比mid位置的值大,那么就到后半部分查找
return bin_search(key,mid+1,high,k); /*在序列的后半部分查找*/
else
return bin_search(key,low,mid-1,k); /*在序列的前半部分查找*/
}
}
主程序调用:weizhi=bin_search(a,0,n-1,x); //假设a数组已从小到大排序。n是数组数据个数,注意要 -1 。x是要找的数据
2、非递归写法:
int bin_search(int key[],int n,int k)// key 源数据数组,假设已从小到大排序 。n:数组数据个数;k:要查找的值
int l=0,r=n-1; // l:数组起点位置 r:数组终点位置,是数据总个数 -1
while(l<=r){
int mid =l+ (r-l) / 2; //如果n不大的话也可以写成:mid=(l+r)/2;
if(key[mid]>k)r=mid-1; //如果要找的数在前半段,那么修改后端点的值
else if(key[mid]<k)l=mid+1; //如果要找的数在后半段,那么修改前端点的值
else return mid; //如果mid位置的值就是要查找的,就把mid的值返回
}
return -1; //如果数组里没有要查找的数据,返回 -1
}
二分法
经常有这样的问题,求xxx最小值的最大值,即求符合条件的值里的最大值(或者求xxx最大值的最小值),这种问题有个解法叫二分答案法。一听,什么,不知道的答案也能二分?嗯没错,关键在于这个答案是可以判断是不是符合条件的。
算法思想
以求最小值的最大值(最小值最大化)为例,尝试一个可能的答案,如果这个答案符合题目条件,那么它肯定是“最小”(可行解),但不一定是“最大”(最优解),然后我们换个更大的可能答案,如果也符合条件,那这个新可行解就更优,不断重复即可。怎么找呢?这时就该二分上场了。
二分前提
1.答案区间上下限确定,即最终答案在哪个范围是容易知道的。
2.检验某值是否可行是个简单活,即给你个值,你能很容易的判断是不是符合题目要求。
3.可行解满足区间单调性,即若x是可行解,则在答案区间内x+1(也可能是x-1)也可行。
二分查找就是二分法的一个简单应用,它也符合二分的三个前提条件:
1.答案区间上下限:就是1到n
2.容易检验是否是答案:只要直接判断二分到的位置的数是否是要查找的数即可
3.区间具有单调性:序列已经是排过序的,明显具有单调性
以本题为例:
设 y=f(x)=ax^3+bx^2+cx+dy=f(x)=ax3+bx2+cx+d 。题目明确说根与根之差的绝对值 >=1 ,那反过来就是说间隔为1的区间内最多只有一个解。
假设|x2-x1|≤1,如果f(x2) × f(x1) < 0 则 x1和x2之间肯定有一个解,然后对这个区间进行二分查找答案。
对比二分法的三个要素均满足:
1.查找的区间限定在间隔为1的区间内(程序里到时枚举所有的区间)
2.对于二分到的数值,只要代入方程求解判断是否满足条件即可。注意实数是不能直接比较相等的,y是实数,不能写y==0,要写成|y|<1e-6 (注意:误差容许范围要根据题目调整,本题要求的精确度是小数点后4位,我们比较的精度要比这个更高才不会出错)
3.x1到x2 之间的间隔0.0001的数明显是有序的;
【题目部分】
给出从小到大排列的n个不同数a[1]~a[n],试判断元素x是否出现在表中。如果在则输出"yes",否则输出“no”
输入
输入有三行。
第一行一个数n。(1 ≤ n ≤ 500000)
第二行有n个数。
第三行为要查找的数。
输出
如果在则输出"yes",否则输出“no”
输入样例 1 输出样例1
6 yes
1 2 3 4 5 6
3
【代码部分】AC代码
【经典例题】
1.【二分】序列划分<OJ>
2.【NOIP2015年提高组题Day2】跳石头 (洛谷传送门)
3.【二分/分治】一元三次方程求解(2)<OJ>
4.[Median-dxz]御坂网络(Links)<OJ2756>