一,概念
二分查找是将数组a [n]的中的N个元素分成大致相等的两部分,取A [N / 2]与待查找值X做比较,如果:
(1)X = a [N / 2],则找到X
(2)X <a [N / 2],则只要在数组左半部分继续搜索。
(3)X> a [N / 2],则只要在数组右半部分继续搜索。
图示:
二分法简而言之就是一种能够快速缩小查找范围的查找方法,每次都是比较目标值与中值的大小,再缩小范围,看是查找左半部分还是右半部分,再以缩小范围后的区间中值为起点查找,只到找到目标值为止。
例:给定一个数组a [101] = {0,1,2,..... 100},求每个元素需通过多少次二分查找才能被找到的次数
代码:
//程序目的,求0 1 ... 99 100 这个数组中每个元素需几次二分查找才能找到的次数
#include<iostream>
using namespace std;
int main() {
for(int i=0; i<=100; i++) {
//左右区间的范围
int left=0,right=100;
//中间值,即查找的起点
int mid;
//查找所需的次数
int cont=0;
//当左区间小于右区间,说明还有数没查找过的时候
while(right>=left) {
//次数增加
cont++;
//初始化起点的位置
mid=(left+right)/2;
//如果该值小于中间值,则缩小区间至[l,mid-1]
if(i<mid) {
right=mid-1;
}
//如果该值大于中间值,则缩小区间至[mid+1,r]
else if(i>mid) {
left=mid+1;
}
//如果猜中,跳出
else {
break;
}
}
cout<<i<<" "<<"一共查找了"<<cont<<"次"<<endl;
}
return 0;
}
运行结果:
运行结果显示,元素50只经过一次二分查找就找到了,不难理解,这是因为50处在数组最中间的位置,第一次就找到了它。
例:从一个数组中找到两个数,使其和为一个指定的数。
遍历数组对每一个a[i],通过二分查找找到另一个a[mid]通过两者之和与目标值m的比较决定缩小区间的范围
这里稍微做了一点变形即将m = a [i] + a [mid]变形为a[mid] = m-a [i],这就符合了二分法的定义。
代码:
#include<iostream>
#include<algorithm>
using namespace std;
int main() {
long a[100000],n,m,i,j;
//标记变量
bool flag=false;
cin>>n;
for(i=0; i<n; i++) {
cin>>a[i];
}
//目标值m
cin>>m;
//将数组排好序
sort(a,a+n);
//遍历数组当且仅当没找到目标值时
for(i=0; i<n; i++) {
//查找区间为[i+1,n-1]
int l=i,r=n-1;
while(l<r) {
int mid=(r+l)/2;
//当找到目标值且不重复时
if(m-a[i]==a[mid]&&i!=mid) {
flag=true;
cout<<a[i]<<" "<<a[mid]<<endl;
break;
}
//否则缩小范围到[mid+1,r]
else if(m-a[i]>a[mid]) {
l=mid+1;
} else {
r=mid-1;
}
}
}
//如果没找到
if(flag==false) {
cout<<"No"<<endl;
}
return 0;
}
运行结果:
二,局限
任何一种算法都有其局限性。二分查找一般来说仅适用于一个元素基本有规律的数组查找,这样才能体现它的快速,当待查找数组毫无规律时,若不排序,二分查找也就失去了它的快速特点。