引用严蔚敏的《数据结构》的话,在所有数量级都为nlgn的排序方法中,快排的常数因子k最小。这意味着快排是目前被认为是最好的一种内部排序方法。

下面先放出快排的代码:

void QuickSort(int *a,int low,int high){

       if(low<high){

              int l = low,h = high,flag = a[low];

              while(l<h){

                     while(l<h&&a[h]>=flag) h--;

                     a[l] = a[h];

                     while(l<h&&a[l]<=flag) l++;

                     a[h] = a[l];

              }

              a[l] = flag;

              QuickSort(a,low,l-1);

              QuickSort(a,l+1,high);

       }

}

 

       所以分析快排的时间性能就很有必要了。影响快排的重要因素是枢轴数的选择。理想情况下,如果每次划分两个子数组的长度都相同,那么效率肯定会是最高的,那如果我们每次都找到数组中的中位数作为枢轴数那就可以了。然而输入的数组本身肯定是无序的,如果要找到中位数就需要有序输入,这个做法肯定不行的,我们对快排的改进必须建立在保证递归里的一次操作时间复杂度为O(n)。

       教科书上给出的方法是选择数组第一个数为枢轴数。

优化一:随机选择枢轴数

当数组部分有序,选择第一个数为枢轴数,快排的比较次数会大幅度增加,而且划分子数组的长度会差异较大,当数组基本有序,快排就蜕变为冒泡了,如果随机选择枢轴数,就可以利用随机性来规避这种情况,下面我们来修改一下代码为:

//要取得[a,b]的随机整数,使用(rand() % (b-a+1))+ a;

void QuickSort_random(int *a,int low,int high){

    if(low<high){

           int random = ((rand()%(high-low+1))+low);

           int l = low,h = high,flag = a[random];

           swap(a[random],a[low]);

           while(l<h){

                  while(l<h&&a[h]>=flag) h--;

                  a[l] = a[h];

                  while(l<h&&a[l]<=flag) l++;

                  a[h] = a[l];

           }

           a[l] = flag;

           QuickSort_random(a,low,l-1);

           QuickSort_random(a,l+1,high);

    }

}

 

改代码有一点需要注意的是,随机选取了一个数,要把这个数的位置放回第一位,然后其余代码跟原来一样就好了(其实我想不懂为什么不放回第一位,按照本来的代码就是错误的,不知道怎么修改才会不出错,希望有大神可以指导我),为了代码的正确性,不管选择哪个数为枢轴数,我都是将这个数放到第一位。

优化二:三者取中选择枢轴数

为了更容易模拟出找到整体数组的中位数,我们可以选取三个数(第一个,最后一个和中间的数),然后取三个数的中位数作为枢轴数,修改后的代码如下:

//取三者中其关键宇取中值的记录为枢轴

void quick_sort_three(int *s, int l, int r) {//取随机数为枢纽

    if (l < r)

    {

         int x = 0;

         int x1 = s[l], x2 = s[r], x3 = s[(l + r) >> 1];

         if (x1 <= x2&&x2 <= x3) {

              x = x2;

             swap(s[r], s[l]);

         }

         else if (x2 <= x1&&x1 <= x3) {

              x = x1;

             swap(s[l], s[l]);

         }

         else {

              x = x3;

             swap(s[(l+r)/2], s[l]);

         }

         int i = l, j = r;

         while (i < j)

         {

             while (i < j && s[j] >= x) // 从右向左找第一个小于x的数 

                 j--;

             if (i < j)

                 s[i++] = s[j];



             while (i < j && s[i] <= x) // 从左向右找第一个大于等于x的数 

                 i++;

             if (i < j)

                 s[j--] = s[i];

         }

         s[i] = x;

         quick_sort_three(s, l, i - 1); // 递归调用  

         quick_sort_three(s, i + 1, r);

    }

}

这时我们来测试一下性能。

(测试环境为vs2017,release,x86,硬件为surfacepro4,i5低压+8G+256G)

输入的数组为随机生成的int整数,共有100组,长度分别为1000,10000,50000,100000

 

选第一个数
100组的平均时间/ms

随机选一个数
100组的平均时间/ms

三者取中
100组的平均时间/ms

STL
sort

规模n

 

 

 

 

1000

0.03

0.09

0.06

0.03

10000

0.71

1.16

0.91

0.95

50000

4.68

5.58

4.14

4.45

100000

8.51

11.06

8.72

9.47

 

快速排序的优化——枢轴数的选择

 

神奇的是,测试结果居然是完全没有优化过的快排最快!随机选择枢轴数的方法最慢!

对于随机选择的方法,我的猜想是可能rand()这个方法消耗的时间比较多所以会变慢,然后三者取中的方法接近STL的sort方法,应该是比较好的优化方法。至于普通的快排为什么会这么快我想不明白,但是必须尊重实验结果,所以打脸的数据我还是贴出来了,欢迎各位下载我的代码测试一下或者提出意见,最后附上测试的代码的github地址,谢谢!

https://github.com/810410738/sort_test/

相关文章:

  • 2022-02-08
  • 2021-11-19
  • 2021-05-13
  • 2021-11-19
  • 2022-12-23
  • 2021-11-20
  • 2022-02-01
猜你喜欢
  • 2022-12-23
  • 2021-12-06
  • 2022-12-23
  • 2022-02-19
  • 2022-12-23
  • 2022-12-23
  • 2022-02-22
相关资源
相似解决方案