数据结构学习 (向量:四(有序向量的查找算法))

有序向量

查找

二分查找(版本A)
//在有序向量[lo, hi)内查找元素e,返回其秩
template <typename T> static Rank binSearch(T* A, T const& e, Rank lo, Rank hi)
{
	while(lo < hi)  //每次迭代至多多两次比较,有三个分支
	{
		Rank mi = (lo + hi)/2;  //以中点为轴点
		if(e < A[mi])   
			hi = mi;     //深入前半段继续查找
		else if(A[mi] > hi)
			lo = mi + 1; //深入后半段继续查找
		else 
			return mi;  //e = mi, 在mi处命中
	}
	return -1;  //查找失败
}

这种二分查找方法采用了减而治之的思想,每次迭代之多经过两次比较,,或者找到目标元素,或者将问题转化为一个规模更小的新问题。
缺点:

  1. 但这种方法有一个问题,就是当向量中可能有个目标元素e时,无法保证返回其中秩最大的那个; 当查找失败时,只简单的返回-1.
  2. 最坏情况下算法的整体性能比较不稳地,主要是因为向左向右深入时不均衡导致的。

复杂度:O(logn)(每次迭代,有效的查找区间宽度将按1/2的比例以几何级数的速度递减)

二分查找(版本B)

改进思路:使算法无论朝着向左深入还是向右深入的方向都只进行一次比较。

template <typename T> static Rank binSearch(T* A, T const& e, Rank lo, Rank hi)
{
	//在有序向量[lo, hi)内查找元素e,返回其秩
template <typename T> static Rank binSearch(T* A, T const& e, Rank lo, Rank hi)
{
	while(1 < hi - lo)  //每步迭代仅需要进行一次比较判断,有两个分支;成功查找不能提前终止
	{
		Rank mi = (lo + hi)/2;  //以中点为轴点
		(e < A[mi] ? hi = mi : lo = mi); //经比较后确定深入[lo, mi)、[mi, hi)哪个区间	
	}
	return (e == A[lo] ? lo : -1);  //查找成功返回对应的秩,失败则返回-1。
}

版本B的算法其复杂度O(logn)不变。在这一版本中,只有当向量有效区间缩短至1是循环才终止。不能如版本A那样,一旦命中就返回。一次最后的情况效率有所下降,但最坏的情况效率相应的有所提高,整体性能更加趋于稳定。

语义约定

以上两种查找算法都没有限制如果目标元素有多个时,返回哪个目标元素的。而查找算法一般都有一个语义约定,即:返回不大于目标元素e的最后一个元素。
也就是分以下两种情况:

  1. 当有多个目标元素e时,应返回最靠后(秩最大)者。
  2. 当查找失败时,应返回小于e的最大者(含哨兵[lo-1])。
版本C

经过以上的语义约定,进行如下改进

template <typename T> static Rank binsearch(T* A, T const& e, Rank lo, Rank hi)
{
	while(lo < hi)    //每步迭代值做一次比较,有两个分支。
	{
		Rank mi = (lo + hi)/2;
		(e < A[mi]) ? hi = mi : lo = mi + 1; //经比较确定进入那个区间[lo, mi),(mi, hi).
	}
	return --lo; //循环结束时,lo为大于e的元素最小秩,故lo - 1即不大于e的元素最大秩。
}
//该算法目标元素有多个时,返回秩最大的那个,当查找失败时,能够返回失败的位置。

版本C与版本B 的差别主要在于循环终止条件变为当有效区间缩减至0时迭代终止。另外在每次转入后端分支时,子向量的左边界去做mi+1而不是mi。
表面上看,可能遗漏元素A[mi],但这种担心大可不必。版本C算法有一下不变性:A[0, lo)中的元素皆不大于e;A[hi, n) 的元素皆大于e。(读者自行验证)
所以循环终止时,lo = hi,此时区间被分成了[0, lo),[hi, n)两部分,由以上的不变性可知:A[lo-1]为不大于e的最大的元素。所有无论是查找成功或失败,只需返回--lo即可。当成功时:--lo就是目标元素;失败时--lo就是小于e的最大的那个元素的秩。
复杂度:O(logn) (遵守了语义约定,且整体性能更加稳定)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值