有序向量Vector

逆序对是指相邻元素之间前者大于后者,即逆序对。

template<typename T>

int Vector<T>::disorder() const

{

    int n=0;

    for(int i=1;i<_size;i++)

        n+=(_elem[i-1]>_elem[i]); 

    return n;  当且仅当n=0的时候,为有序向量

}

(1)唯一化,uniquify( )


1.低效率的算法

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;

}

运行时间取决于while循环,但是每次调用remove()都会出现复杂度,O(n-1)~O(1),是算数级数,最后结果是O(n2)和无序的deduplicate一样,显然是不满意的。

2.高效率算法:希望能成批的删除,而不是每次remove一个。


template<typename T>

int Vector<T>::uniquify()

{

    Rank i=0,j=0;

    while(++j<_size)  //先加1,也就是从1开始

        if(_elem[i]!=_elem[j])

            _elem[++i]=_elem[j];//else相同直接返回,否则将值移动到++i

    _size=++i;shrink();

    return j-i;   //返回删除的元素个数  

}


没有显示的删除操作,只是在赋值过程,共计n-1次迭代,累计O(n)时间

(2)有序向量:二分查找binSearch( )

约定:找到不大于e的最后一个元素,如元素非常小,返回lo-1,假想左侧有哨兵lo-1位-。(有多个元素命中时,返回其中最靠后的;失败时,小于e的最大值,含哨兵lo-1)


版本A原理,减而治之,以任一元素x=S[mi]为界,将待查找区分为三部分S[lo,mi)<=S[mi]<=S(mi,hi);若e<x,则e必属于左侧子区间;若e>x,则e必属于右侧自区间,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; //如果e小于A[mi],前[lo,mi)继续查找递归

        else if(A[mi]<e) lo=mi+1;// e小于A[mi],后(mi,hi)继续查找递归

        else return mi;//在mi处命中

    }

    return -1;

}

T(n)=T(n/2)+O(1)=O(logn),底数是无所谓的,所以不用纠结啦…

 

查找长度search length:比较得到对于左侧的代价比右侧的代价会少些。而下面介绍的斐波那契算法则是利用这个进行优化的,黄金分割点…


(3)有序向量:斐波那契Fibonacci( ),它的平均查找长度是优于二分查找的。因为查找左侧的步数会比较低,所以偏向左侧。

斐波那契Fibonacci( ):0 1 1 2 3 5 8 13 21…

F(0) = 0,F(1)=1,F(n)=F(n-1)+F(n-2) (n≥2),显然这是一个线性递推数列,随着数列项数的增加,前一项与后一项之比越来越逼近黄金分割点0.618

若n=fib(6)-1=7,下一步分为f(5)-1、f(4)-1也就是4、2;4由可以分为2、1。


template<typenameT>

static Rankf ibSearch(T*A,Tconst&e,Ranklo,Rankhi)

{

    Fibfib(hi-lo);  //一个斐波那契数列

    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;

    }

}

(4)两者的分析

二分查找对应的λ为0.5,而斐波那契对应的λ为黄金分割点0.618.

5)二分查找左右转向代价不平衡的问题,


这与二分查找的不同是mi的x涵盖到后半部分:e<x时,e必属于左侧子区间[lo,mi),若e>x时,e必属于右侧子区间[mi,hi),当hi-lo=1时才命中。因为我们的区间定义都是[lo,hi)

版本B

template<typenameT>

static Rank(T*A,Tconst&e,Ranklo,Rankhi)

{

    while(1<hi-lo)     //查找区间宽度缩短为1时才能终止算法

    {

        Rank mi=(lo+hi)>>1;   //移位操作

        (if(e<A[mi]))?hi=mi:lo=mi;    //判断e和A[mi]的大小

    }

    return(e=A[lo])?lo:-1;  //返回命中元素的秩或者返回-1

}

总体而言比之前的版本A更加稳定。

语义约定:(1)当有多个命中元素时,必须返回最大秩者;(2失败时返回小于e的最大者,即是哨兵lo-1)

版本C

template<typenameT>

static Rank(T*A,Tconst&e,Ranklo,Rankhi)

{

    while(lo-hi)

    {

        Rank mi=(lo+hi)>>1;

        (if(e<A[mi]))?hi=mi:lo=mi+1;   // A[lo=hi]为不大于e的最小元素

    }

    return--lo;   //lo-1即是不大于e的元素的最大秩

}

(6)起泡排序bubbleSort逐趟做扫描交换,直至全序,

由于在某些条件下,前缀是有序的,就不需要逐个进行比较。

template<typenameTvoid Vector<T>::bubbleSort(Ranklo,Rankhi)

{

 while(!bubble(lo,hi--));//逐趟做扫描交换,直至全序

} 

template<typenameT>bool Vector<T>::bubble(Ranklo,Rankhi)

{

bool sorted=true;   //设初始的标志为true,即整体有序标志

while(++lo<hi)

if(_elem[lo-1]>_elem[lo])

{    //如果逆序,则交换,并且false

sorted=false;

swap(_elem[lo-1],_elem[lo]);

}

return sorted;    //返回有序标志

}

继续改进:在已经进行了一段时间的比较之后,后半段已经是有序的,但是前半段有可能是无序的,如果进行之前的算法需要进行扫描O(n),这显然是浪费的,所以我们要想办法进行改进,我们后半段既然已经排序好了,我们就不进行


template<typenameT>  void  Vector<T>::bubbleSort(Ranklo,Rankhi)

{

    while(lo<(hi=bubble(lo,hi)));  //只交换某一部分

}

template<typenameT> Rank  Vector<T>::bubble(Ranklo,Rankhi)

{

    Ranklast=lo;

    while(++lo<hi)

    if(_elem[lo-1]>_elem[lo])

    {

       last=lo;   //更新最右侧逆序的位置

       swap(_elem[lo-1],_elem[lo]);

    }

}

return last;

}

 

(7)归并排序MergeSort,之前的比较算法的上下界分别是Ω(nlogn),上界是O(n2),原理是,序列一分为二O(1),子序列递归排序,合并有序子序列。


求T(n)=2T(n/2)+O(n),其时间复杂度是O(nlogn)。

template<typenameT> 

void Vector<T>::mergeSort(Ranklo,Rankhi)

{

    if(hi-lo<2)  return;

    int mi=(lo+hi)>>1;   //找到中点mi

    mergeSort(lo,mi);    //对前半部分排序,找到递归基

    mergeSort(mi,hi);    //对后部分进行排序找到递归基

    merge(lo,mi,hi);

}

合并后的数据放在A中,

template<typenameT>void Vector<T>::merge(Ranklo,Rank mi,Rankhi)

{

    T*A=_elem+lo;  //_elem[lo,hi)=A[0,hi-lo),A为_elem中的一段

    int lb=mi-lo;

    T*B=newT[lb]; //B长度是lb

    for(Ranki=0;i<lb;B[i]=A[i++]) //将A中的前部分元素复制给B

    int lc=hi-mi;

    T*C=_elem+mi;      //C为A中元素的后半部分

    for(Ranki=0,j=0,k=0;(j<lb)||(k<lc))

    {

       if((j<lb)&&((lc<=k)||(B[j]<=C[k])))A[i++]=B[j++];//比较,C完结或B 不大   

   if((k<lc)&&((lb<=j)||(C[k]<B[j])))A[i++]=C[k++]; //B完结或者C较小

    }

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值