第四课80多分钟,好长啊...内容很多,但是Leiserson教授讲得既快又好!
快速排序(Quicksort)是C. A. R. Hoare于1960年发明,当时他正在莫斯科大学(Soviet Union, 前苏联吧)作访问学生。在一个国家物理实验室的机器翻译项目里,为了能更快的匹配一个已排序的俄英字典,他开发了这个算法来排序要翻译的单词。
快速排序也是使用的分治法的思想。同时也是个in place的算法,这个词意思是指就地进行的算法,不需要额外的存储,想了半天也没想到好的中文对应的字,后来去看了下中文版的书才知道,原来是翻成“就地”,很贴切啊!经过改进的快速排序也是很实用的排序算法,是当前世界上使用最多的排序算法之一。
分治法步骤:
- Divide: 从数组中选择一个元素作为主元,以主元为界将数组分成(PARTITION)两个子数组使得较小的子数组中的元素都小于主元,较大的子数组中的元素都大于主元。
- Conquer: 递归地排序两个子数组。
- Conbine: 因为两个子数组已经有序,这一步没有工作要做。
所以关键步骤就是这个PARTITION:
PARTITION(A, p, q) x := A[p] i := p for j := p+1 to q do if A[j] <= x then i := i+1 swap(A[i], A[j]) swap(A[p], A[i]) return i
这个循环的不变量可以用下面的图表示:
x | <=x | >=x | ? |
p i j q
对于有n个元素的数组的来说,PARTITION的渐近运行时间为Theta(n)。
主程序QUICKSORT很简单:
QUICKSORT(A, p, q) if p < q then r := PARTITION(A, p, q) QUICKSORT(A, p, r-1) QUICKSORT(A, r+1, q) Init call: QUICKSORT(A, 1, n)
分析:在分析中我们假定数组中所有元素都是不同的。首先是Worst case。最坏情况会在输入已经有序或者反序的情况下出现,也就是会使PARTITION分割的子数组中有一个为空的情况下,记为0:n-1,这样就有
T(n) = T(0) + T(n-1) + Theta(n) = Theta(n^2)。
最坏情况下并没有比Insertion sort好!如果你画出上面这个式子的递归树会发现这棵是高度的不平衡的。我们再来看看最好情况,如果我们很幸运,每次PARTITION都能将数组分成两个长度相等的子数组,也就是(n-1)/2 : (n-1)/2。那么有
T(n) = 2T((n-1)/2) + Theta(n) = Theta(nlgn)。这也是Quicksort的下界。
再假定每次分割都是1/10 : 9/10, T(n) = T(n/10) + T(9n/10) + Theta(n),可以画出这个递归树,或者用替换法都能解出T(n) = Theta(nlgn)。仍然是nlgn!
或者假定既“幸运”又“不幸运”:
L(n) = 2U(n/2) + Theta(n) // 幸运,分割成n/2 : n/2
U(n) = L(n-1) + Theta(n) // 不幸运,分割成0 : n-1
还是可以解得L(n) = 2(L(n/2-1) + Theta(n/2)) + Theta(n) = Theta(nlgn)。
也就是,就算有一些步骤是“不幸运”的,我们仍然能得到Theta(nlgn)的渐近运行时间。所以也就有了:
随机版的快速排序(Randomized Quicksort)。
- 运行时间不依赖于输入的排序情况
- 最坏情况仅由随机数的生成决定
通常的做法是随机地选择主元(pivot)。
随机算法的分析一般都比较难。这里的分析都很直观也都是很基础的内容,但是要想到这些东西却不是那容易的,你需要一定的知识体系!
T(n)为渐近运行时间,其期望值(Expectation)为E[T(n)],我们希望对所有的输入,找出这个期望值。
对k=0,1,2,...,n-1,我们定义一个0-1随机变量X_k:
X_k = 1 // 如果PARTITION分割成k:n-k-1
0 // 如果不是的话
X_k的期望E[X_k] = 1*Pr{X_k = 1} + 0*Pr{X_k=0} = Pr{X_k=1} = 1/n。而T(n)可以写成:
这个递归式有n种情况,我们可以借助0-1变量来处理:
这样E[T(n)]可以写成:
因为E[T(0)]=Theta(1),E[T(1)]=Theta(1),为了计算方便,将最后一个和式中的k=0,k=1吸收到Theta(n)中去:
剩下的就是使用替换法证明E[T(n)] <= anlgn,a>0。这里可以看出我们将k=0,1吸收到Theta(n)中的好处,因为lg0无法处理,lg1=0。我们还需要用到一个事实,它的证明需要一点点技巧,也需要一定的篇幅,这里先给出来直接使用:
将E[T(k)]代入E[T(n)]可得:
第二个不等式用到了上面的事实。所以E[T(n)] = Theta(nlgn)。即使不需要调整优化,快速排序也会比一般的排序快上3到4倍,而实用中的快速排序通常会做一些调整,比如QUICKSORT主程序最后一步是尾递归,或者对较小的数组使用直接的排序而不再使用递归等等。
另外,Robert Sedgewick在1975年的Ph.D论文就是Quicksort (Quicksort. Ph.D. thesis. Stanford Computer Science Report STAN-CS-75-492. Stanford, CA: Stanford University, May 1975.) 还有他在1978年发在ACM上的"Implementing Quicksort Programs" (Implementing Quicksort Programs. Comm. ACM 21, 847-857, 1978.) Donald Knuth的TAOCP第三卷Sorting and searching等等。总之快速排序的材料很多。Wiki上的也值得一读(http://en.wikipedia.org/wiki/Quicksort)。