你真的会二分查找吗?

二分查找可以说是最常用的有序表查找算法了。Knuth在TAOCP中提到,第一个二分查找程序在1946年已经公布,但是到了1962年才出现第一个没有BUG的二分查找程序,期间经历了16年的时间。我们今天就来深入学习一下这一经典的算法。需要说明的是,我们默认数组都是非递减序列。
先给出二分查找递归版本和非递归版本的基本代码。

#include<iostream>  
using namespace std;  
  
int BinarySearch(int a[],const int &x,int l,int r)  
{
	if(l<=r) 
	{
		int m=(l+r)/2;
		if(a[m]==x) return m;           
 		else if(a[m]>x) return BinarySearch(a,x,l,m-1);   
  		else return BinarySearch(a,x,m+1,r); 
	}
	else return -1;
}      
  
int main()  
{  
    int array[10]={10,20,30,40,50,60,70,80,90,100};  
	cout<<BinarySearch(array,90,0,9)<<endl; 
}
#include<iostream>  
using namespace std;  
  
int BinarySearch(int a[],const int &x,int l,int r)  
{  
    while(l<=r)  
    {   
        int m=(l+r)/2;
        if(x==a[m]) return m; 
		else if(x<a[m]) r=m-1;   
        else l=m+1;  
    }  
    return -1;  
}      
  
int main()  
{  
    int array[10]={10,20,30,40,50,60,70,80,90,100};  
    cout<<BinarySearch(array,95,0,9)<<endl;  
}
STL已经实现了二分查找算法。binary_search在查找成功时返回1,查找失败时返回0。
#include<algorithm>  
#include<iostream>  
#include<vector>  
using namespace std;  

int main()  
{  
    vector<int> v;  
    for(int i=1;i<=20;i++)
	{  
        v.push_back(i);  
    }
    sort(v.begin(),v.end());
    cout<<binary_search(v.begin(),v.end(),3)<<endl;    
    cout<<binary_search(v.begin(),v.end(),30)<<endl;  
}  

我们也可以用二分查找找寻边界值,也就是说在有序数组中找到正好大于/小于/大于等于/小于等于目标数的那个数。在STL中:
ForwardIter upper_bound(ForwardIter first,ForwardIter last, const _Tp& val)算法返回一个非递减序列[first, last)中第一个大于val的位置。
ForwardIter lower_bound(ForwardIter first,ForwardIter last,const _Tp& val)算法返回一个非递减序列[first, last)中的第一个大于等于值val的位置。

你真的会二分查找吗?_第1张图片

实现这两个算法最重要需要注意的是查找成功以后要继续查找;只需要判断两种情况而不是前面的三种。

#include<iostream>
#include<algorithm>
using namespace std;

int my_lower_bound(int *array,int size,int key)
{
    int first=0,last=size-1;
    int middle,pos=size; 
	//pos记录结果,没有找到时返回size      
    while(first<last)
    {
		middle=(first+last)/2;
        if(array[middle]<key)
		{     
            first=middle+1;
        }
        //如果array[middle]<key则middle一定不是解,first=middle+1
        else
		{
            last=middle;          
            pos=last;              
        }
        //如果array[middle]>=key则middle可能是解,记录middle的值,last=middle
    }
    return pos;
}

int my_upper_bound(int *array,int size,int key)
{
    int first=0,last=size-1;
    int middle,pos=size;
	//pos记录结果,没有找到时返回size   
    while(first<last)
    {
        middle=(first+last)/2;
        if(array[middle]<=key)
		{    
             first=middle+1;  
        }
        //如果array[middle]<=key则middle一定不是解,first=middle+1    
        else
		{
			last=middle;
            pos=last;   
        }
        //如果array[middle]>key则middle可能是解,记录middle的值,last=middle
    }
    return pos;
}

int main()
{
	int array[10]={2,2,3,3,3,4,4,5,88,88};
	cout<<lower_bound(array,array+10,4)-array<<endl;
	cout<<my_lower_bound(array,10,4)<<endl;
	cout<<upper_bound(array,array+10,0)-array<<endl;
	cout<<my_upper_bound(array,10,0)<<endl;
}
在STL中还有一个与二分查找有关的函数叫equal_range,该函数返回的是一对迭代器,第一个迭代器指向所查找元素的第一次出现的位置,第二个迭代器指向所查找元素最后一次出现位置的后一个位置。我们可以大致把equal_range理解成lower_bound+upper_bound。原理差不多,我们只讲解一下基本用法。
int main()  
{  
    vector<int> vecInt;  
    pair<vector<int>::iterator,vector<int>::iterator> iter;  
    vecInt.push_back(1);  
    vecInt.push_back(2);  
    vecInt.push_back(3);  
    vecInt.push_back(3);  
    vecInt.push_back(4);  
    vecInt.push_back(5);  
    iter=equal_range(vecInt.begin(),vecInt.end(),3);  
    cout<<*iter.first<<endl;  
    cout<<*iter.second<<endl;  
}  
输出:
3
4
如果vecInt里面没有3呢?
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;

int main()  
{  
    vector<int> vecInt;  
    pair<vector<int>::iterator,vector<int>::iterator> iter;  
    vecInt.push_back(1);  
    vecInt.push_back(2);  
    vecInt.push_back(4);  
    vecInt.push_back(5);  
    iter = equal_range(vecInt.begin(),vecInt.end(),3);  
    cout<<*iter.first<<endl;  
    cout<<*iter.second<<endl;  
}  
输出:
4
4
如果我们查找5,输出是这样的:

你真的会二分查找吗?_第2张图片

这是因为迭代器指向了容器的end。所以我们最好在使用之前做一下判断。

#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;

int main()  
{  
	vector<int> vecInt;  
    pair<vector<int>::iterator,vector<int>::iterator> iter;  
    vecInt.push_back(1);  
    vecInt.push_back(2);  
    vecInt.push_back(4);  
    vecInt.push_back(5);  
    iter=equal_range(vecInt.begin(),vecInt.end(),0); 
 	if(iter.first!=vecInt.end()) cout<<*iter.first<<endl; 
    if(iter.second!=vecInt.end()) cout<<*iter.second<<endl;    
}  


你可能感兴趣的:(你真的会二分查找吗?)