快速排序

快速排序(Quick   Sort)称划分交换排序。其基本思想是:在当前无序区R[1]到R[h]到中任取一个记录作为比较的“基准”(不妨记为temp),用此基准将当前无序区划分为左右两个较小的无序子区:R[1]到R[i-1]和R[i+1]到R[h],且左边的无序子区中记录的关键字均小于或等于基准temp的关键字,右边的无序子区中记录的关键字均大于或等于基准temp的关键字,而基准temp则位于最终排序的位置上   

R[1]到R[i-1]中关键字≤temp.key=R[i+1]到R[h]的关键字(1≤i≤h) 
当R[1]到R[I-1]和R[I+1]到R[h]均非空时,分别对它们进行上述的划分过程,直至所有无序子区中记录均已排好序为止。 
要完成对当前无序区R[1]到R[h]的划分,具体做法是:设置两个指针i和j,它们的初值分别为i=1和j=h。不妨取基准为无序区的第1个记录R[i](即R[1]),并将它保存在变量temp中。令j自h起向左扫描,直到找到第1个关键字小于temp.key的记录R[j],将R[j]移至i所指的位置上(这相当于交换了R[j]和基准R[i](即temp)的位置,使关键字小于基准关键字的记录移到了基准的左边);然后,令i自i+1起向右扫描,直至找到第1个关键字大于temp.key的记录R[i],将R[i]移至   j指的位置上(这相当于交换了R[j]和基准R[i](即temp)的位置,使关键字大于基准关键字的记录移到了基准的右边);接着,令j自j+1起向右扫描,如此交替改变扫描方向,从两端各自往中间靠拢,直至i=j时,i便是基准x的最终位置,将x放在此位置   上就完成了一次划分。   

综合上面的叙述,下面分别给出一次划分及其排序的算法。 
int   partition(r,1,h)     /*返回划分后被定们的基准记录的位置*/ 
rectype   R[   ];     /*对无序区R[1]到R[h]做划分*/ 
int   1,h; 
{int   i,j; 
      rectype   temp; 
      i=1;j=h   temp=R[i];       /*初始化,temp为基准*/ 
      Do{ 
              While((R[j].key> =temp.key)     &&   (i <j)) 
          j--;     /*从右向左扫描,查找第1个关键字小于temp.key的记录*/ 
        if(i <j)   R[i++]=R[j];   /*交换R[i]和R[j]*/ 
    while((R[i].key <=temp.key)   &&   (i <j)) 
          i++;   /*从左向左扫描,查找第1个关键字大于temp.key的记录*/ 
        if(i <j)   R[j--]=R[i];     /*交换R[i]和R[j]*/ 
    }       
quicksort(R,s1,t1)   /*对R[s1]到R[t1]*/ 
rectype   R[   ]; 
int   s1,t1; 
{int   i; 
    if   (s1 <t1)     /*   只有一个记录或无记录须排序*/ 
      {i=   partition   (R,s1,t1);     /*对R[s1]到R[t1]做划分*/ 
        quicksort   (R,s1,i-1);     /*递归处理左区间*/ 
        quicksort   (R,i+1,t1);     /*递归处理右区间*/ 
      } 
}   

图9-7展示了一次划分的过程及整个快速排序的过程。图中方括号表示无序区,方框表示基准temp的关键字,它未参加真正的交换,只是在划分完成时才将它放入正确的位置上。 
初始关键字           [[49   ]   38     65     97     76     13     27     49`] 
                                
                                                          i                                                       j 
j向左扫描           [[49]     38     65     97     76     13     27     49   `] 
&nbsp; 
                                                      i                                                 j 
第一次交换后       [27       38     65     97     76     13     [   ]     49`] 
    
                                                                    i                                       j 
i向右扫描             [27       38     65     97     76     13     [   ]     49   `] 
    
                            i                                 j 
第二次交换后                       [27       38     [   ]     97     76     13     65     49   `] 
&nbsp; 
                                                                          i                       j 
j向左扫描,位置不变   
第三次交换后                       [27       38     13     97     76     [   ]     65     49]`] 
&nbsp; 
i向左扫描,位置不变,                                         i                 j 
第四次交换后                       [27       38     13     [   ]     76     97     65     49   `] 
&nbsp; 
                                                                                  i           j 
j   向左扫描                           [27       38     13     [49]     76     97     65     49   `] 
                                                              
                                                                                    i     j 

初始关键字:                   [49     38     65     97     76     13     27     49`] 
一趟排序之后:               [27     38     13]     49     [76     97     65     49`] 
二趟排序之后:               [13]     27     [38]     49   [49`   65]     76     [97] 
三趟排序之后:               13     27     38     49     49`     [65]     76     97     
最后的排序结果:           13     27     38     79     79`     65     76     97 
(b)   各趟排序之后的状态   

最坏情况是第次划分选取的基准都是当前无序区中关键字最小(或最大)的记录,划分的基准左边的无序子区为空(或右边的无序子区为空),而划分所得的另一个非空的无序子区中记录数目,仅仅比划分前的无序区中记录个数减少一个。因此,快速排序必须做n-1趟,每一趟中需进行n-i次比较,故总手工艺比数次数达到最大值: 
            Cmax=∑   (n-i)=n(n-1)/2=O(n2) 
显然,如果按上面给出的划分算法,每次取当前无序区的第1个记录为基准,那么当文件的记录已按递增序(或递减序)排列时,每次划分所取的基准就是当前无序区中关键字最小(或最大)的记录,则快速排序所需的比较次数反而最多。 
在最好情况下,每次划分所取的基准都是当前无序区的“中值”记录,划分的结果是基准的左、右两个无序子区的长度大致相等地。设C(n)表示对长度为n的文件进行快速排序所需的比较次数,显然,它应该等于对长度为n的无序区进行划分所需的比较次数n-1。加上递归地对划分所得的左、右两个无序子区(长度≤n/2)进行快速排序所需的比较总人 
数。   

假设文件长度n=2k,那么总的比较次数为: 
C(n)   ≤n+2C(n/2) 
          ≤n+2[n/2+2C(n/22)]=2n+4C(n/22) 
          ≤2n+4[n/4+2C(n/23)]=3n+8C(n/23) 
          ≤…… 
          ≤kn+2kC(n/2k)=nlog2n+nC(1) 
          =O(nlog2n) 
注意:式中C(1)为一常数,k=log2n。   , 

因为快速排序的记录移动次数不大于比较的次数,所以,快速排序的最坏时间复杂度应为O(n2),最好时间复杂雅兴O(log2n)。为了改善最坏情况下的时间性能,可采用三者取中的规则,即在每一趟划分开始前,首先比较R[1].key,R[h].key和R[[(1+h)/2]].key,令三者中取中值的记录和R[1]交换之。 
可以证明:快速排序的平均时间复杂度也是O(nlog2n),它是目前基于比较的内部排序方法   中速度最快的,快速排序亦因此而得名。   
快速排序需要一个栈空间来实现递归。若每次划分均能将文件均匀分割为两部分,则栈的最大深度为[log2n]+1,所需栈空间为O(log2n)。最坏情况下,递归深度为n,所需栈空间为O(n)。 
快速排序是不稳定的,请读者自行检验。  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值