统一接口:
template <typename T>//查找算法统一接口,0<= lo < hi <= _size
Rnak vector<T>::search(T const & e,Rank lo,Rank hi) const{
return (rand()%2)?//按1/2的概率随机选用算法
binSearch(_elem, e, lo, hi);//二分查找算法
:fibSearch(_elem, e, lo, ho);//斐波那契查找算法
}
A二分查找:
减而治之(二分(折半)策略)。将区间分为3部分:
S[lo, mi)<=S[mi]<=S(mi, hi);
记x=S[mi].查找目标为e.
e < x :则e 在左侧区间S[lo, mi),通过递归深入查找。
e > x :则e 在右侧区间S(mi, hi),通过递归深入查找。
e = x :则e==x,返回值即可。
每次经过比较将问题规模至少减少一半(甚至直接找到)
时间复杂度是O(log 2 n )
template <typename T> 0<= lo <=hi <=user_size_t
static Rank binSearch(T* A, T const & e, Rank lo,Rank hi){
while(lo < hi){
Rank mi = (lo+hi)>>1; //以中点划分区间
if(e<A[mi]) hi=mi; //深入左半区间查找
else if(A[mi] <e) lo= mi+1; //深入右半区间查找
else return mi; //mi处即是所找对象
}
return -1; //查找失败,在整个区间没找到对象e
}
A斐波那契查找:
斐波那契查找的时间复杂度还是O(log 2 n ),但与折半查找相比,斐波那契查找的优点是它只涉及加法和减法运算,而不用除法,理论上比折半查找小,但是还是得视具体情况而定。
template <typename T> 0<= lo <=hi <=user_size_t
static Rank fibSearch(T* A, T const & e, Rank lo,Rank hi){
Fib fib(hi-lo); //创建Fib数列
while(lo <hi){
while(hi-lo<fib.get()) fib.prev();
Rank mi = lo +fib.get() -1; //按黄金比例切分
if(e<A[mi]) hi=mi; //深入左半区间查找
else if(A[mi] <e) lo= mi+1; //深入右半区间查找
else return mi; //mi处即是所找对象
}
return -1; //查找失败,在整个区间没找到对象e
}
B二分查找(改进):
mi同样是中心轴点,查找对象为e,x=S[mi]
如果e < x : 则深入查找[lo, mi)
如果e >=x: 则深入查找[mi,hi)
template <typename T> 0<= lo <=hi <=user_size_t
static Rank binSearch(T* A, T const & e, Rank lo,Rank hi){
while(hi- lo > 1){ //区间缩小为1时停止
while(hi-lo<fib.get()) fib.prev();
Rank mi = (lo+hi)>>1; //以中点划分区间
e< A[mi]? hi= mi :lo= mi;
}
return A[lo]==e? lo : -1; //判断是否找到
}
A插值查找:
如果有序向量中各元素是均匀独立分布的,于是[lo, hi)中的元素大小大致成线性趋势增长。
于是我们通过秩的比大致等于值的比:
来猜测e的大致位置
mi=lo+(hi- lo)*[(e- A[lo]) / (A[hi]- A[lo])];
从而提高区间收敛的速度。
时间复杂度是O(loglog n)
template <typename T> 0<= lo <=hi <=user_size_t
static Rank binSearch(T* A, T const & e, Rank lo,Rank hi){
Rank mi=lo+(hi- lo)*[(e- A[lo]) / (A[hi]- A[lo])];
while(mi>=lo){ //mi<lo时停止
if(e==A[mi]) return mi;
else if(e>A[mi])
lo=mi+1;
else
hi=mi;
Rank mi=lo+(hi- lo)*[(e- A[lo]) / (A[hi]- A[lo])];
}
return -1; //查找失败
}