1、有序性及其甄别
有序/无序序列中,任意/总有一对相邻元素顺序/逆序
相邻逆序对的数目,可以度量向量的逆序程度。
template <typename T>
int Vector<T>::disordered() const{
int n=0;
for(int i=1;i<_size;i++){
n+=(_elem[i-1]>_elem[i]);
}
return n;
}
2、唯一化:低效算法
template <typename T>
int Vector<T>::uniquify(){
int oldSize=_size;
int i=0;
while(i<_size-1)
(_elem[i]==_elem[i+1])?remove(i+1):i++;
return oldSize-_size;
}
最坏时,累计o(n2)
最好时,o(n)
3、唯一化:高效算法
template <typename T> int Vector<T>::uniquify(){
Rank i=0,j=0;
while(++j<_size){
if(_elem[i]!=_elem[j])_elem[++i]=_elem[j];
}
_size=++i;shrink();
return j-i;
}
累计时间o(n);
4、有序向量的查找:二分查找
template <typename T> Rank Vector<T>::search(T const & e,Rank lo,Rank hi) const{
return (rand()%2)?binsearch(_elem,e,lo,hi):fibsearch(_elem,e,lo,hi);
}
5、语义约定
至少,应该便于有序向量自身的维护:V.insert(1+V.search(e), e)
即便失败,也应给出新元素适当的插入位置,若允许重复元素,则每一组也需按其插入的次序排列。
约定:在有序向量区间[lo,hi)中,确定最后一个不大于e的最后一个元素:
若负无穷<e<V[lo],则返回lo-1(左侧哨兵)
若V[hi-1]<e<正无穷,则返回hi-1(末元素=右侧哨兵左临)
6、二分查找原理
减而治之:以任一元素x=S[mi]为界,都可将待查找区间分为三部分,S[lo,mi)<=S[mi]<=S(mi,hi),只要将目标元素e与x比较,即可确定更小的范围,甚至可直接命中,随即返回。
算法实现:
template <typename 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;
}
return -1;//查找失败
}
复杂度o(logn),再精细一点,约为o(1.50logn)
7、Fibonacci查找
二分查找的版本仍有改进余地,主要原因是左右分支前的关键码比较次数不同,而递归深度却相同。
比如,若设n=fib(k)-1,则可取mi=fib(k-1)-1;于是前后子向量的长度分别为fib(k-1)-1、fib(k-2)-1;
template <typename T> static Rank binSearch(T* A, T const& e, Rank lo, Rank hi){
Fib fib(hi-lo);
while(lo<hi){
while(hi-lo<fib.get())fib.prev();
Rand mi=lo+fib.get()-1;
if(e<A[mi])hi=mi;
else if(A[mi]<e)lo=mi+1;
else return mi;
}
return -1;//查找失败
}