全面掌握快速排序

1、快排算法的主要思想在于分治:

对a[l...r],对其进行合适的重排,从而找到一个q,使数组重排后的a[q]满足:a[l]...a[q-1] <=a[q] ,a[q+1]...a[r]>=a[q],这样a[q]这个数必然在其正确的位置上。

a[q]把a数组分为两半,再重复对这两半运用这个思想。这就是快排。


2、如何重排及确定a[q]。

这里有两种实现起来的思想。难度都差不多。

一种叫挖坑填数:这种称呼详见http://blog.csdn.net/morewindows/article/details/6684558

思想是先令x=a[l];作为基准数。

维护两个指针,一个i,一个j,初始i=l,j=r。

在满足i<j的情况下使j--,找到第一个a[j]<x(也可以是大于等于,这关系到快排的优化问题,后面详述),若i<j则让a[i]=a[j],i++。注意此时j变成了坑,而i位置已经填好故让i++。

满足i<j时,让i++。找到第一个a[i]>x,若i<j则让a[j]=a[i],j--。

注意出循环的时候,i一定等于j的,这就是x正确的位置(正确的坑)。

这种方法,说起来比较烦,实现起来也容易错,但是便于优化。

详见后面代码。

一种叫就地重排。

后者比前者实现起来简单点。

先使x=a[r];

然后维护两个指针i,j,使得a[i]....a[j]都是比x大的数。

初始时i=l;j=l-1;

然后j++,并且j一直循环到r。

在这个过程中,若a[j]>x,那什么都不做。

a[j]<=x,则交换a[i]和a[j],然后让i++。这个里面有个精髓的地方在于最后,a[j]=a[p]时时,也会把a[p]换到前面去,对于a[r],正好把它放在了正确的位置。所以用这种方法编程要严格注意边界问题。这样也就确定了中间数a[q]的位置。简单想想就知道q=i-1。


3、挖坑填数式快排实现代码:

void qsort(int a[],int l,int r)
{
     if (r<=l) return;
     int i=l,j=r,x=a[l],t;
     while (i<j)
     {
           while (i<j a="" j="">=x) j--;
           if (i<j){
                      a[i]=a[j];
                      i++;
           }
           while (i<j && a[i]<=x) i++;
           if (i<j){
                      a[j]=a[i];
                      j--;
           }
     }
     a[i]=x;
     qsort(a,l,i-1);
     qsort(a,i+1,r);
}</j>

4、就地重排式快排实现代码:

void qsort(int a[],int l,int r)
{
     if (r<=l) return;
     int i=l,j=l,x=a[r],t;
     for (;j<=r;j++)
         if (a[j]<=x){
                          t=a[i];a[i]=a[j];a[j]=t;
                          i++;
                     }
     qsort(a,l,i-2);
     qsort(a,i,r);
}


5、效率问题

不管哪种方法,一趟重排的效率都是O(n),真正效率上的区别在于能不能把划分选在中间的位置,所以每次中间数的选择至关重要

快排当数组顺序原来就排好的时候(或全部是重复元素),会退化成n^2的复杂度。

原因在于,每次划分,都把数组划为n-1和0两个部分。



6、算法优化

对挖坑填数式快排进行优化。

首先对于顺序已经排好的情况,可以使用中间数作为x。每次把中间数与开头的数交换,再应用同样的方法就行了。

但这对于某些具有特殊设计顺序的数据,还是会退化。

解决方法是:

1、随机化取x

2、每次比较中间、开头、结尾三个数,取中间数为x。

但还有一种情况算法耗时还比较多。

那就是所有数都是重复元素的情况。

这里的精髓在于while (i<x && a[j]>=x) j--;这句话,把a[j]>=x改成a[j]>x,然后对应的while (i<j && a[i]<=x) i++;中a[i]<=x改为a[i]<x。这样,程序在遇到相同的数时,也会发生交换,最后确定x的位置在数组的中间,不会发生退化的情况。这点写程序的时候务必要注意!

还有一个优化是,当p-r比较小时,针对小数组采用插入排序来减少时间,不赘述了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值