快速排序

Posted fengzeng666

tags:

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

1、选主元

如果每次都选取第一个元素为主元,则时间复杂度为O(n^2)。

技术分享图片

所以建议主元选取方法为:选头中尾元素的中位数。(三数中值分割法)

 

2、代码实现(三数中值分割法)

技术分享图片

 

 1 #define Cutoff 100
 2 
 3 void Swap(ElementType *a, ElementType *b)
 4 {
 5     ElementType tmp = *a;
 6     *a = *b;
 7     *b = tmp;
 8 }
 9 
10 //选主元
11 ElementType Median3( ElementType A[], int Left, int Right )
12 {
13     int Center = (Left + Right) / 2;
14     
15     if(A[ Left ] > A[ Center ])
16         Swap( &A[ Left ], &A[ Center ] );
17     if(A[ Left ] > A[ Right ])
18         Swap( &A[ Left ], &A[ Right ] );
19     if(A[ Center ] > A[Right])
20         Swap(&A[ Center ], &A[ Right ]);
21         
22     /*此时A[Left] <= A[Center] <= A[Right] ,所以三者中位数(即主元)为A[Center]*/
23     Swap(&A[ Center ], &A[ Right-1 ]); // 将主元藏在最右边, 即A[Right]的左侧
24     
25     /*A[Left]此时肯定小于A[Center], A[Right]肯定大于A[Center], 
26       并且两者已经分居在主元的两侧,又把主元藏在了A[Right-1], 
27       所以只需考虑A[Left+1]与A[Right-2]之间的元素*/
28     return A[ Right-1 ];  //返回主元
29 }
30 
31 //快速排序的递归函数
32 void QSort( ElementType A[], int Left, int Right )
33 {
34     int Pivot, i, j;
35     if( Right-Left >= 3 )    //如果序列元素充分多,进入快排
36     {
37         Pivot = Median3( A, Left, Right );   //选主元
38  /*a*/  i = Left;
39         j = Right - 1;
40         while(1)     //将序列中比主元小的移动到主元左边,大的移动到右边
41         {
42  /*b*/      while( A[++i] < Pivot ) {}       
43             while( A[--j] > Pivot ) {}      
44             if( i < j ) 
45                 Swap( &A[i], &A[j] );
46             else
47                 break;
48   }
49         Swap( &A[i], &A[Right-1] );    //此时i左边的元素都比主元小,右边的都比主元大,所以i为主元的正确位置
50         QSort( A, Left, i-1  );        //递归解决左边
51         QSort( A, i+1, Right );        //递归解决右边
52     }
53     else
54         InsertionSort( A+Left, Right-Left+1 );   //元素个数小于等于2时,必须用简单排序处理
55 }
56 
57 
58 //快速排序的统一接口
59 void QuickSort( ElementType A[], int N )
60 {
61     QSort( A, 0, N-1 );
62 }

 

 注意

( 1 ) 不能把a, b处的程序改为

i = Left + 1; 
j = Right - 2;
while( A[i] < Pivot )   ++i;
while( A[j] > Pivot )   --j;

否则将出错,因为当A[ i ] = A[ j ] = pivot 的时候,会产生一个无限循环 ( 死循环 ). 

 

( 2 ) 当A[ i ] 或 A[ j ]与pivot时,i和j必须停下来做交换,否则如果数组中全部元素都相等的话,i 将一直移动到倒数第二个位置,

这样将会使一趟过后主元只向左移动一个位置,使得运行时间变为O(n^2).

 

( 3 ) Cutoff的值至少要为3,否则对两个数或一个数无法进行三数中值的选择,且只有一个数的时候,Left与Right相等。数组会越界。

当间隔( Right - Left )小于2时,必须进行简单排序,不然快速排序会出现错误。

因为:

(1)当 Right - Left 等于 0时,快排的元素个数只有一个,当这个数位于数组的最后位置时,当++i后,再访问A[ i ], 就会出现数组越界的错误!

 

(2) 当 Right - Left 等于 1时,快排的元素个数只有2个,假设这两个数是4,5且为数组最后两个数,那么选出的主元就是4,当 --j 后,A[ j ]就会访问到4前面的那个数,

 但你只是对4,5进行快排,当j访问到前面的数时, 就导致快排出现错误的结果。且如果这两个数是数组前两个数的话,--j 就会导致数组越界。

 

(3)快速排序只能将小于主元的数放在主元左边,大于主元的数放在右边,某次快排后把5,4排在了主元3的右边,但并不能继续把5和4排好序。所以此时必须借助简单排序。比如,

      若缺少下面这条简单排序的语句, 比如对A[8] = { 8, 1, 4, 9, 0, 3, 5, 2},快速排序将会出现错误,输出为0,1,2,3,5,4,8,9。

     else
           InsertionSort( A+Left, Right-Left+1 );

 

 

3、快速排序存在的问题

技术分享图片

 

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

算法排序之堆排序

前端开发工具vscode如何快速生成代码片段

前端开发工具vscode如何快速生成代码片段

如何使用sublime代码片段快速输入PHP头部版本声明

代码片段如何使用CSS来快速定义多彩光标

vs2003:快速片段工具