快速排序 本页面将简要介绍快速排序。
简介 快速排序(英语:Quicksort),又称分区交换排序(英语:partition-exchange sort),简称快排,是一种被广泛运用的排序算法。
基本原理与实现 原理 快速排序的工作原理是通过 分治  的方式来将一个数组排序。
快速排序分为三个过程:
将数列划分为两部分(要求保证相对大小关系); 递归到两个子序列中分别进行快速排序; 不用合并,因为此时数列已经完全有序。 和归并排序不同,第一步并不是直接分成前后两个序列,而是在分的过程中要保证相对大小关系。具体来说,第一步要是要把数列分成两个部分,然后保证前一个子数列中的数都小于后一个子数列中的数。为了保证平均时间复杂度,一般是随机选择一个数 
之后,维护一前一后两个指针 
其实,快速排序没有指定应如何具体实现第一步,不论是选择 
第三步中的序列已经分别有序且第一个序列中的数都小于第二个数,所以直接拼接起来就好了。
实现(C++)  1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 // C++ Version 
struct   Range   {    int   start ,   end ;    Range ( int   s   =   0 ,   int   e   =   0 )   {   start   =   s ,   end   =   e ;   } }; template   < typename   T > void   quick_sort ( T   arr [],   const   int   len )   {    if   ( len   <=   0 )   return ;    Range   r [ len ];    int   p   =   0 ;    r [ p ++ ]   =   Range ( 0 ,   len   -   1 );    while   ( p )   {      Range   range   =   r [ -- p ];      if   ( range . start   >=   range . end )   continue ;      T   mid   =   arr [ range . end ];      int   left   =   range . start ,   right   =   range . end   -   1 ;      while   ( left   <   right )   {        while   ( arr [ left ]   <   mid   &&   left   <   right )   left ++ ;        while   ( arr [ right ]   >=   mid   &&   left   <   right )   right -- ;        std :: swap ( arr [ left ],   arr [ right ]);      }      if   ( arr [ left ]   >=   arr [ range . end ])        std :: swap ( arr [ left ],   arr [ range . end ]);      else        left ++ ;      r [ p ++ ]   =   Range ( range . start ,   left   -   1 );      r [ p ++ ]   =   Range ( left   +   1 ,   range . end );    } } 
实现(Python)  1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 # Python Version 
def  quick_sort ( alist ,  first ,  last ): 
    if  first  >=  last : 
        return 
    mid_value  =  alist [ first ] 
    low  =  first 
    high  =  last 
    while  low  <  high : 
        while  low  <  high  and  alist [ high ]  >=  mid_value : 
            high  -=  1 
        alist [ low ]  =  alist [ high ] 
        while  low  <  high  and  alist [ low ]  <  mid_value : 
            low  +=  1 
        alist [ high ]  =  alist [ low ] 
    alist [ low ]  =  mid_value 
    quick_sort ( alist ,  first ,  low  -  1 ) 
    quick_sort ( alist ,  low  +  1 ,  last ) 
性质 稳定性 快速排序是一种不稳定的排序算法。
时间复杂度 快速排序的最优时间复杂度和平均时间复杂度为 
对于最优情况,每一次选择的分界值都是序列的中位数,此时算法时间复杂度满足的递推式为 
对于最坏情况,每一次选择的分界值都是序列的最值,此时算法时间复杂度满足的递推式为 
对于平均情况,每一次选择的分界值可以看作是等概率随机的。下面我们来证明这种情况下算法的时间复杂度是 
引理 1:  当对 
由于在每次划分元素的过程中,都会选择一个元素作为分界,所以划分元素的过程至多发生 
设 
显然每次选取的分界值是不同的,而元素只会和分界值比较,所以总比较次数
由期望的线性性,
和 比 较 引理 2:  
先证必要性,即若 
若 
再证充分性,即若 
不失一般地,假设 
考虑计算 和 比 较 
和 比 较 或 是 集 合 中 第 一 个 被 选 中 的 分 界 值 所以
和 比 较 由此,快速排序的期望时间复杂度为 
优化 朴素优化思想 如果仅按照上文所述的基本思想来实现快速排序(或者是直接照抄模板)的话,那大概率是通不过 P1177【模板】快速排序  这道模板的。因为有毒瘤数据能够把朴素的快速排序卡成 
所以,我们需要对朴素快速排序思想加以优化。较为常见的优化思路有以下三种
通过 三数取中(即选取第一个、最后一个以及中间的元素中的中位数)  的方法来选择两个子序列的分界元素(即比较基准)。这样可以避免极端数据(如升序序列或降序序列)带来的退化; 当序列较短时,使用 插入排序  的效率更高; 每趟排序后,将与分界元素相等的元素聚集在分界元素周围 ,这样可以避免极端数据(如序列中大部分元素都相等)带来的退化。 下面列举了几种较为成熟的快速排序优化方式。
三路快速排序 三路快速排序(英语:3-way Radix Quicksort)是快速排序和 基数排序  的混合。它的算法思想基于 荷兰国旗问题  的解法。
与原始的快速排序不同,三路快速排序在随机选取分界点 
三路快速排序在处理含有多个重复值的数组时,效率远高于原始快速排序。其最佳时间复杂度为 
三路快速排序实现起来非常简单,下面给出了一种三路快排的 C++ 实现。
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 // C++ Version 
// 模板的T参数表示元素的类型,此类型需要定义小于(<)运算 
template   < typename   T > // arr 为需要被排序的数组,len 为数组长度 
void   quick_sort ( T   arr [],   const   int   len )   {    if   ( len   <=   1 )   return ;    // 随机选择基准(pivot) 
   const   T   pivot   =   arr [ rand ()   %   len ];    // i:当前操作的元素 
   // j:第一个等于 pivot 的元素 
   // k:第一个大于 pivot 的元素 
   int   i   =   0 ,   j   =   0 ,   k   =   len ;    // 完成一趟三路快排,将序列分为: 
   // 小于 pivot 的元素| 等于 pivot 的元素 | 大于 pivot 的元素 
   while   ( i   <   k )   {      if   ( arr [ i ]   <   pivot )        swap ( arr [ i ++ ],   arr [ j ++ ]);      else   if   ( pivot   <   arr [ i ])        swap ( arr [ i ],   arr [ -- k ]);      else        i ++ ;    }    // 递归完成对于两个子序列的快速排序 
   quick_sort ( arr ,   j );    quick_sort ( arr   +   k ,   len   -   k ); } 
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 # Python Version 
def  quick_sort ( arr ,  l ,  r ): 
    if  l  >=  r : 
        return 
    random_index  =  random . randint ( l ,  r ) 
    pivot  =  arr [ random_index ] 
    arr [ l ],  arr [ random_index ]  =  arr [ random_index ],  arr [ l ] 
    i  =  l  +  1 
    j  =  l  
    k  =  r  +  1 
    while  i  <  k : 
        if  arr [ i ]  <  pivot : 
            arr [ i ],  arr [ j  +  1 ]  =  arr [ j  +  1 ],  arr [ i ] 
            j  +=  1 
            i  +=  1 
        elif  arr [ i ]  >  pivot : 
            arr [ i ],  arr [ k  -  1 ]  =  arr [ k  -  1 ],  arr [ i ] 
            k  -=  1 
        else :  
            i  +=  1 
    arr [ l ],  arr [ j ]  =  arr [ j ],  arr [ l ] 
    quick_sort ( arr ,  l ,  j  -  1 ) 
    quick_sort ( arr ,  k ,  r ) 
内省排序 内省排序(英语:Introsort 或 Introspective sort)是快速排序和 堆排序  的结合,由 David Musser 于 1997 年发明。内省排序其实是对快速排序的一种优化,保证了最差时间复杂度为 
内省排序将快速排序的最大递归深度限制为 
从 2000 年 6 月起,SGI C++ STL 的 stl_algo.h 中 sort() 函数的实现采用了内省排序算法。
线性找第 k 大的数 在下面的代码示例中,第 
找第 
我们可以借助快速排序的思想解决这个问题。考虑快速排序的划分过程,在快速排序的「划分」结束后,数列 
可以证明,在期望意义下,程序的时间复杂度为 
实现(C++)  1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 // 模板的 T 参数表示元素的类型,此类型需要定义小于(<)运算 
template   < typename   T > // arr 为查找范围数组,rk 为需要查找的排名(从 0 开始),len 为数组长度 
T   find_kth_element ( T   arr [],   int   rk ,   const   int   len )   {    if   ( len   <=   1 )   return   arr [ 0 ];    // 随机选择基准(pivot) 
   const   T   pivot   =   arr [ rand ()   %   len ];    // i:当前操作的元素 
   // j:第一个等于 pivot 的元素 
   // k:第一个大于 pivot 的元素 
   int   i   =   0 ,   j   =   0 ,   k   =   len ;    // 完成一趟三路快排,将序列分为: 
   // 小于 pivot 的元素 | 等于 pivot 的元素 | 大于 pivot 的元素 
   while   ( i   <   k )   {      if   ( arr [ i ]   <   pivot )        swap ( arr [ i ++ ],   arr [ j ++ ]);      else   if   ( pivot   <   arr [ i ])        swap ( arr [ i ],   arr [ -- k ]);      else        i ++ ;    }    // 根据要找的排名与两条分界线的位置,去不同的区间递归查找第 k 大的数 
   // 如果小于 pivot 的元素个数比k多,则第 k 大的元素一定是一个小于 pivot 的元素 
   if   ( rk   <   j )   return   find_kth_element ( arr ,   rk ,   j );    // 否则,如果小于 pivot 和等于 pivot 的元素加起来也没有 k 多, 
   // 则第 k 大的元素一定是一个大于 pivot 的元素 
   else   if   ( rk   >=   k )      return   find_kth_element ( arr   +   k ,   rk   -   k ,   len   -   k );    // 否则,pivot 就是第 k 大的元素 
   return   pivot ; } 
参考资料与注释 build 本页面最近更新:2022/3/5 14:02:44 ,更新历史 edit 发现错误?想一起完善? 在 GitHub 上编辑此页! people 本页面贡献者:NachtgeistW , Alisahhh , Backl1ght , billchenchina , Enter-tainer , iamtwz , Ir1d , Konano , ksyx , mcendu , ouuan , partychicken , sbofgayschool , sshwy , StudyingFather , Xeonacid , xzcxzcyy , zhangjunyan2580 copyright 本页面的全部内容在 CC BY-SA 4.0  和 SATA