数据结构--向量--二分查找

我们这里来介绍一下二分查找

本页内容:

        1.二分查找原理

        2.代码实现

        3.查找长度分析

        4.在向量模板中实现

        5.二分查找改进

1.二分查找原理:

          设查找范围为[lo,hi)

          二分查找采用减而治之的办法,以有序数列的中点元素V[mi]为界,将查找区间分为三部分:

          V[lo,mi)<=V[mi]<=V(mi,hi)

          再将目标元素e与V[mi]作比较,可分为三种情况进一步处理:

          1) e<V[mi] :则深入左侧V[lo,mi)继续查找。

          2) e>V[mi] :则深入右侧V(mi,hi)继续查找。

          3) e=V[mi] :已找到,立即返回

          这样,每次查找都可以将查找范围缩小一半。可用下图形象表示:

          

2.代码实现:

#include<iostream>
using namespace std;
/*********二分查找(基于数组)**********/
int binSearch(int *A,int e,int lo,int hi)
{
	int mi;
 	while(lo<hi)
 	{
 	  mi=(lo+hi)/2;
 	  if(e<A[mi])//深入左半段 
 	  {
 	  	hi=mi;
	  }
	  else if(e>A[mi])//生如右半段 
	  {
	   	lo=mi+1;
	  }
	  else
	  {
	   	return mi;//命中返回 
	  }
	}
	
	return -1;//查找失败 
}
/**************************************/ 
int main()
{
	int a[100];
	for(int i=0;i<100;i++)
	{
		a[i]=i;
	}
	/*binSearch查找测试*/ 
	cout<<binSearch(a,101,0,100)<<endl;
	cout<<binSearch(a,50,0,100)<<endl;
    /*******************/	 
	return 0;
} 
            在这里我写了一个数组的二分查找算法,向量的与之类似。毕竟向量可以说是抽象化的数组,这段代码大家可以直接拿去运行检验。

3.查找长度分析

       在此算法中,我们将比较次数作为查找长度。很明显,查找长度越小,说明效率越高。

       通常,我们评价一个算法的好坏,需分别针对其成功与失败查找,从最好,最坏,平均等角度评估。

       如图,我们取有序数列长度为7,来分别观察其各个元素的的查找长度:

       

      由代码:

         if(e<A[mi])//深入左半段 
 	  {
 	  	hi=mi;
	  }
	  else if(e>A[mi])//深入右半段 
	  {
	   	lo=mi+1;
	  }
	  else
	  {
	   	return mi;//命中返回 
	  }
       易知,其向左侧深入只需一次判断,向右侧深入需两次判断,判断命中需两次判断。故可容易求得:

       查找成功时,各元素对应的成功查找长度 :{4,3,5,2,5,4,6}            平均查找长度:(4+3+5+2+5+4+6)/7=4.14

       查找失败时,各元素对应的失败查找长度 :{3,4,4,5,4,5,5,6}         平均查找长度:(3+4+4+5+4+5+5+6)/8=4.50
       我们也可以改写一下程序,让其为我们自动最终查找长度:

       代码如下:

       

#include<iostream>
using namespace std;
/*********二分查找(基于数组)**********/
int binSearch(int *A,int e,int lo,int hi)
{
	int count=0;//统计查找长度 
	int mi;
 	while(lo<hi)
 	{
 	  mi=(lo+hi)/2;
 	  if(e<A[mi])//深入左半段 
 	  {
 	  	count++; 
 	  	hi=mi;
	  }
	  else if(e>A[mi])//深入右半段 
	  {
	  	count+=2;
	   	lo=mi+1;
	  }
	  else
	  {
	  	count+=2;
	   	return count;//命中返回 
	  }
	}
	
	return count;//查找失败 
}
/**************************************/ 
int main()
{
	int a[100];
	for(int i=0;i<7;i++)
	{
		a[i]=i;
	}
	/*成功查找长度*/
	cout<<"各元素成功查找长度:" ; 
	for(int i=0;i<7;i++)
	{
		cout<<binSearch(a,i,0,7)<<" "; 
    }
	cout<<endl;
	/*失败查找长度*/
	cout<<"各元素失败查找长度:";
	for(int i=0;i<7;i++)//重新给a赋值,为失败查找做准备a={1,3,5,7,9,11,13} 
	{
		a[i]=1+2*i;
    }
	for(int i=0;i<8;i++)
	{
		cout<<binSearch(a,i*2,0,7)<<" ";//查找数列为{0,2,4,6,8,10,12,14} 
    }
	cout<<endl;	 
	return 0;
} 
         运行结果如下:

         

4.在向量模板中实现

    将二分查找用于向量中,并与search()接口对接:

    我自己写过一篇关于向量模板的文章,此博文章是那篇博文的的分支,是search()接口的其中一个实现。现在我们将二分查找,写成向量的模式与那篇博文中的search()对接。下面给出那篇博文的连接,有兴趣的话,大家可以看一下。

    链接:向量模板介绍

    代码实现:

template<typename T>
Rank myVector<T>::binSearch(T*A,T const&e,Rank lo,Rank hi) const 
{
	Rank mi;
    while(lo<hi)//每步迭代可能要做两次比较判断,有3个分支 
    {
	    mi=(lo+hi)>>1;//以中点为轴点 
    	if(e<A[mi]) hi=mi;//深入前半段[lo,mi)继续查找 
    	else if(A[mi]<e) lo=mi+1;//深入后半段(mi,hi) 
    	else return mi;//在mi处命中 
	}
	if(e<A[mi])
	{
		return mi-1;//查找失败
	}
	else
	{
		return mi;//查找失败
	}
		 
}

  该代码并不严格遵循search()函数的接口规则,即返回有序数列中值不大于e且秩最大的元素。下面改进后的算法会严格遵循search()接口。

5.二分查找改进

   改进思路:由于上述算法中向左向右查找的代价不同,向左只需一次判断,而向右只需两次判断,造成了向右查找的代价过高。我们可以想着将mi并入右边,这样就只需一次

                       判断。可用下图形象表示:

                       

                      代码实现:

template<typename T>
Rank myVector<T>::binSearch(T*A,T const&e,Rank lo,Rank hi) const 
{
	Rank mi;
    while(lo<hi)//每步迭代可能要做两次比较判断,有3个分支 
    {
	while(lo<hi)
	{
		Rank mi=(lo+hi)/2;//以中点为轴点,经比较后确定深入
		if(e<A[mi])
		{
			hi=mi;//[lo,mi) 
		 } 
		 else
		 {
		 	lo=mi+1;//(mi,hi)
		 }//出口时,A[lo=hi]为大于e的最小元素 
	 } 
	 return --lo;//因此--lo是不大于e的元素的最大秩
		 
}
         上述代码严格遵照这search接口规范,右比其它算法更加简洁,效率更高,是最合适的二分算法。建议大家多用这个二分算法。




想要学习更多关于向量的知识,请点击!!!!!





    

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值