快速排序的优化

Posted algorithm-process

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了快速排序的优化相关的知识,希望对你有一定的参考价值。

  在我的上一篇博客中(https://www.cnblogs.com/algorithm-process/p/11963856.html),我们提到了快速排序划分的三钟方法,在快速排序中,我们希望对序列进行划分时,能将一个序列进行两等分,但是我们在使用时始终选取第一个元素为基准值,这样就会导致在一些情况(比如是降序排列的序列)下,无法将序列进行等分或是近似等分。如果我们每次选择基准值是一个中间值是不是能尽可能的将待排序的序列做到一个趋近于等分的情况呢?接下来就是由这种思想而提出的优化方法。

 三点中值法

  思想:在左边界p,右边界r和中间下标mid所代表的元素之间选择一个作为基准点,选择方法为找到这三个值中中间那个作为基准点。

  代码如下:

 1 int partition(int a[],int p,int r){
 2     //选取一个基准点 
 3     int midIndex= p+((r-p)>>1);//中间下标
 4     int midValue=-1;//中间值下标
 5     if(a[p]<=a[midIndex]&&a[p]>=a[r]) midValue=p;
 6     else if(a[r]<=a[midIndex]&&a[r]>=a[p]) midValue=r;
 7     else midValue=midIndex;
 8     swap(a[midValue],a[p]);
 9     
10     int x=a[l];//基准点
11     int l=p+1,q=r;
12     while(l<=q){
13         while(a[l]<=x&&l<=q) ++l;
14         while(a[q]>x&&l<=q) --q;
15         if(l<=q) swap(a[l],a[q]);
16      }
17      swap(a[p],a[q]);
18      return q;
19 }

   在这三个点中找中间值作为基准点在某些情况下也并不能代表整个序列的中间值,为了能得到一个真正的中间值,我们在此基础上又提出了进一步求中间值的方法。

 绝对中值法

  思想:将一个数组分成每五个元素一组,若最后一组元素不足五个,也作为一组。然后使用一个数组来存储每个划分中的中间值,得到每个划分的中间值后,取这个数组的中间值,即为所求的中间值。

  得到中间值的代码如下:

 1 void insertsort(int a[],int p,int q){//插入排序 
 2     int temp;
 3     for(int i=p;i<=q;i++){
 4         if(a[i]<a[i-1]){
 5             temp=a[i];
 6             for(int j=i;j>=0;j--){
 7                 if(j>0&&a[j-1]>temp) a[j]=a[j-1];
 8                 else {
 9                     a[j]=temp;
10                     break;
11                 }
12             }
13         }
14     }
15 }
16 int getmid(int a[],int p,int r){
17     int length=r-p+1;
18     int grouplength=(length%5==0)?(length/5):(length/5+1);//每五个元素一组,得到划分的个数 
19     int *medians=new int[grouplength];//定义存储中间值的数组
20     int midIndex=0;
21     for(int i=0;i<grouplength;i++){
22         if(i==grouplength-1){//最后一组时 ,需要单独处理,因为这组中元素个数小于5个 
23             insertsort(a,p+j*5,r);//调用插入排序 
24             medians[midIndex++]=a[(p+j*5+r)/2];//存入数组 
25         }else {
26             insertsort(a,p+j*5,p+j*5+4);
27             medians[midIndex++]=a[(p+j*5+2)];
28         }
29     } 
30     insertsort(mediams,0,grouplength-1);
31     return medians[grouplength/2];
32 } 

 

 

   这种方法得到中间值的时间复杂度为O(n),但是通常我们并不使用它,更多的是使用三点中值法。

 当序列中元素较少时,使用插入排序

  思想:虽说插入排序的时间复杂度为O(n2),但是实际上是:n(n-1)/2。快速排序的时间复杂度为O(nlogn),实际上是:n(logn+1)。经过计算后发现当n<=8时,插入排序所消耗的时间是少于快速排序的。

  经过优化后的代码是:

  

 1 quicksort(int a[],int l,int r){//快排递归形式 
 2     if(l<r){
 3         if((r-l+1)<=8) insertsort(a,l,r);//调用插入排序
 4         else{
 5             int q=partition(a,l,r);//找到中间数,不一定是中位数 
 6             quicksort(a,l,q-1);
 7             quicksort(a,q+1,r);            
 8         } 
 9     }
10 }

 

 

 上述就是比较常用的三种优化方法了。

以上是关于快速排序的优化的主要内容,如果未能解决你的问题,请参考以下文章

决战西二旗|快速排序的优化

决战西二旗|快速排序的优化

不同场景下 快速排序的几种优化方式你懂不?

冒泡排序与快速排序

冒泡排序与快速排序

快速排序(Quick Sort)及优化