算法导论Lecture 4:Quicksort

第四课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)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值