快速排序(Quick Sort)详解

[003]:http://latex.codecogs.com/svg.latex?$\Theta(\textit%20n\cdot%20log_2(n))$
[004]:http://latex.codecogs.com/svg.latex?$H(X)%20=%20-\sum_{i}{}P(X_i)%20\cdot%20log_2(P(X_i))$
[005]:http://latex.codecogs.com/svg.latex?i%20\in%20[1,n!],%20\forall%20i\in[1,n!]%20%20%20%20P(X_i)%20=\frac{1}{n!}
[006]:http://latex.codecogs.com/svg.latex?H(X)%20=%20-n!\cdot%20\frac{1}{n!}log_2{\frac{1}{n!}}=log_2{(n!)}
[007]:http://latex.codecogs.com/svg.latex?log_2{(n!)}=\Theta(n\cdot%20log_2(n))
[008]:http://latex.codecogs.com/svg.latex?\Theta(n\cdot%20log_2(n))
[009]:http://latex.codecogs.com/svg.latex?-p\cdot%20log_2{p}-(1-p)\cdot%20log_2{(1-p)}
[010]:http://latex.codecogs.com/svg.latex?\frac{\Theta(n\cdot%20log_2(n))}{1}=\Theta(n\cdot%20log_2(n))
[011]:http://latex.codecogs.com/svg.latex?\Theta(n^2)%20%20and%20%20\Theta(n)
[012]:http://latex.codecogs.com/svg.latex?\Omega(n)%20%20and%20%20O(n^2)%20%20and%20%20\Theta(n)
[013]:http://latex.codecogs.com/svg.latex?\Theta(n%20\cdot%20log_2(n))%20%20and%20%20\Theta(n%20\cdot%20log_2(n))
[014]:http://latex.codecogs.com/svg.latex?\Omega(n%20\cdot%20log_2(n))%20%20and%20%20O(n^2)%20%20and%20%20\Theta(n)
[015]:http://latex.codecogs.com/svg.latex?\Theta(n%20\cdot%20log_2(n))
[016]:http://latex.codecogs.com/svg.latex?E(n)=\frac{1}{n}(E(0)+E(n-1))+\frac{1}{n}(E(1)+E(n-2))+...+\frac{1}{n}(E(n-1)+E(0))+n+1=\frac{2}{n}\sum_{i=0}^{n-1}E(i)+n+1%20\Rightarrow
[017]:http://latex.codecogs.com/svg.latex?n%20\cdot%20E(n)=2\cdot%20\sum_{i=0}^{n-1}E(i)+(n+1)\cdot%20n
[018]:http://latex.codecogs.com/svg.latex?(n-1)%20\cdot%20E(n-1)=2\cdot%20\sum_{i=0}^{n-2}E(i)+n\cdot%20(n-1)
[019]:http://latex.codecogs.com/svg.latex?n%20\cdot%20E(n)-(n-1)%20\cdot%20E(n-1)=2\cdot%20E(n-1)+2%20\cdot%20n
[020]:http://latex.codecogs.com/svg.latex?n%20\cdot%20E(n)=(n+1)%20\cdot%20E(n-1)+2%20\cdot%20n
[021]:http://latex.codecogs.com/svg.latex?a_n%20\cdot%20E(n)=b_n%20\cdot%20E(n-1)+c_n
[022]:http://latex.codecogs.com/svg.latex?a_n=n,b_n=n+1,c_n=2\cdot%20n
[023]:http://latex.codecogs.com/svg.latex?s_n%20\cdot%20a_n\cdot%20E(n)=s_n%20\cdot%20b_n\cdot%20E(n-1)+s_n%20\cdot%20c_n
[024]:http://latex.codecogs.com/svg.latex?S_n=s_n\cdot%20a_n%20%20and%20%20S_{n-1}=s_n\cdot%20b_n
[025]:http://latex.codecogs.com/svg.latex?S_n\cdot%20E(n)=S_{n-1}\cdot%20E(n-1)+s_n\cdot%20c_n
[026]:http://latex.codecogs.com/svg.latex?S_n\cdot%20E(n)=S_0\cdot%20E(0)+\sum_{i=0}^{n}s_i\cdot%20c_i
[027]:http://latex.codecogs.com/svg.latex?S_n=s_n\cdot%20a_n%20,S_{n-1}=s_n\cdot%20b_n\Rightarrow%20S_{n-1}=s_n\cdot%20b_n=s_{n-1}\cdot%20a_{n-1}\Rightarrow
[028]:http://latex.codecogs.com/svg.latex?s_n=\frac{s_{n-1}\cdot%20a_{n-1}}{b_n}
[029]:http://latex.codecogs.com/svg.latex?s_n=\frac{a_{n-1}\cdot%20a_{n-2}\cdot%20...\cdot%20a_{1}}{b_n\cdot%20b_{n-1}\cdot%20...\cdot%20b_{2}}
[030]:http://latex.codecogs.com/svg.latex?s_n=\frac{2}{(n+1)\cdot%20n}
[031]:http://latex.codecogs.com/svg.latex?\frac{2}{n+1}\cdot%20E(n)=S_0\cdot%20E(0)+\sum_{i=1}^{n}\frac{4}{i}
[031]:http://latex.codecogs.com/svg.latex?\frac{2}{n+1}\cdot%20E(n)=S_0\cdot%20E(0)+\sum_{i=1}^{n}\frac{4}{i}
[032]:http://latex.codecogs.com/svg.latex?E(n)=2\cdot%20(n+1)\cdot\sum_{i=1}^{n}\frac{1}{i}
[033]:http://latex.codecogs.com/svg.latex?\sum_{i=1}^{n}\frac{1}{i}=log_2(n)+O(1)
[034]:http://latex.codecogs.com/svg.latex?E(n)=\Theta(n\cdot%20log_2(n))
算法研究是计算机科学中重要的一个分支。众多基础算法中,比较排序算法是基础中的基础。本文主要介绍一种经典的比较排序算法----快速排序算法(由Tony Hoare于1961年发表)的基本思路,优点以及时间和空间复杂度分析。

比较排序算法

概念

简单而言,比较排序算法是对两个元素进行比较来决定两个元素的在输出序列中相对位置(谁在前面谁在后面)的排序算法。从数学意义说,比较排序算法要求序列中的元素满足如下两条性质


  • 比较排序算法类似用天平来比较两个物体之间谁的质量大小来排序


    快速排序(Quick Sort)详解_第1张图片
    800px-Balance_à_tabac_1850.JPG

图片来自:由Photographie personnelle User:Poussin jean - objet personnel User:Poussin jean,CC BY-SA 3.0,https://commons.wikimedia.org/w/index.php?curid=1681347

时间复杂度下界

首先抛出结论,比较排序算法的下界是![][003] n为序列长度。证明方法有很多,算法导论中用的是决策树模型证明(The decision-tree model)。我在这里想展示另外一种证明方法:信息熵(因为我是通信狗出身,虽然现在已经拜在图灵的门下,但香农仍是我的祖师爷啊)。
熵值的数学表达为![][004]如果序列长度为n,那么对序列排序后则可能有 n! 种结果。如果输入序列是完全随机序列,则每种结果的概率是相等的。具体到比较排序算法和之前的熵值公式![][005]带回熵值公式![][006]算法导论中3.19证明了![][007]也就是说,针对排序而言,一个长度为n的序列,其蕴含的信息量为![][008]接下来我们分析一下一次比较操作所消除的信息量。对于任意的a和b,假设 p 为 a < b 的概率,那么一次比较操作所能消除的信息量C为![][009]对于随机序列,p = 0.5,C=1,这也是C能取到的最大值。所以想用比较操作来完成序列排序,至少要![][010]次比较操作。

常见的比较排序算法

  • 冒泡排序(Bubble Sort)时间复杂度和空间复杂度为![][011]
  • 插入排序(Insertion Sort) 时间复杂度下界,上界以及空间复杂度为![][012]
  • 归并排序(Merge Sort)时间复杂度和空间复杂度为![][013]可见虽然归并排序的时间复杂度符合理论最小值,但它的空间复杂度也很高。更蛋疼的是归并排序的时间复杂度分析是建立在RAM模型(Random Access Memory)上,而实际的计算机内存模型并不是RAM而是有cache的。因为归并排序算法在每次递归迭代过程中都会申请新的内存然后在新申请内存上进行操作。这不匹配于cache内存机制,所以归并排序算法在真实硬件上的表现不很理想。不过随着并行,分布式系统的兴起,这个算法又有了新的生机。它非常适合并行优化,并且已经有了相应分析和研究(如果这篇反响好我第二篇就写这个内容)。

快速排序算法

伪码

快速排序则充分利用了cache机制,算法的伪码是

algorithm quicksort(A, lo, hi) 
    if lo < hi then p := partition(A, lo, hi) 
    quicksort(A, lo, p – 1) 
    quicksort(A, p + 1, hi) 
algorithm partition(A, lo, hi)
    pivot := A[hi] 
    i := lo
    for j := lo to hi – 1 do 
    if A[j] ≤ pivot then 
        swap A[i] with A[j] 
        i := i + 1 
    swap A[i] with A[hi] 
    return i

复杂度分析

显然,快速排序的时间复杂度的下界,上界以及空间复杂度为![][014]看上去并不怎么样。但因为输入序列为随机序列,所以我们还得关注下时间复杂度的期望值。
在算法导论中,快速排序的时间复杂度期望值证明思路如下

  • 猜测其时间复杂度的期望值为![][015]
  • 再用数学归纳法证明

另一种分析方法

但作为强迫症晚期的我总觉得这个方法不够酷炫,因为猜测时间复杂度期望值这一步总有一种碰运气的感觉(当然这是调侃的说法,其实这种思路在数学证明中有时候很有用)。基于此,向大家隆重推出下面这种方法,简直是狂拽酷炫屌炸天(纯个人主观喜好。。。。)。假设E(n)为输入序列长度为n时期望的时间复杂度,则![][016]![][017]那么![][018]两式相减![][019]![][020]我们将这个等式一般化![][021]![][022]接下来我们将一般化后的等式两边变形为![][023]我们假设这个参数选择得非常好,巧妙的满足![][024]所以等式可以变形为![][025]化简![][026]![][027]![][028]迭代则有![][029]我们将![][022]代入后得到![][030]![][031]因为E(0)=0![][032]从算法导论的A.7可知![][033]所以![][034]
上述证明来自于具体数学(concrete mathematics)第二章

总结

时间复杂度的期望值符合理论最小值,并且空间复杂度只有O(n),能够很好的利用cache机制。所以快速排序是很不错的串行比较排序算法,特别是在真实的硬件上,所以gcc中还有qsort这个库函数(stdlib.h)。

第一次用Markdown,输入公式好累啊。。。。。

你可能感兴趣的:(快速排序(Quick Sort)详解)