第四讲:分治法(续)
4.1 快速排序(Quicksort)
1这是一种分治法实现
2这是原地(“in place”)排序,即不需要额外的存储空间
3该算法很实用
算法步骤:
—Devide,把输入序列分成两个子序列,左边的子序列小于等于pivot,右边的序列大于pivot,即如下图:
—Conquer,递归地用上述方法对两个子序列进行排序;
—Combine,trivial(无足轻重),因为是已经排列好了的。
Merge Sort本质上是递归地Merging,而Quicksort本质上是递归地Partitioning(分成子序列)
关键步骤:划分(Partitioning)
伪代码:
对于n个元素的划分的时间复杂度为:
T(n) = Theta(n),即是线性时间复杂度
那么,有了划分的子程序,分别对两个子序列进行递归调用即可,Quicksort的伪代码实现为:
上述是一种尾递归方法,可以使用一些尾递归方法对其做优化(关于递归,在《算法精解C语言描述》里有一章专门讲述基础,有兴趣的可以去参考下);对其可以做小的改进,但是核心思想不变。
代码实现:可以参照此前的一篇文章,《六种排序算法分析及C语言实现》。
http://blog.csdn.net/ai552368625/article/details/37904631
4.2 算法分析
4.2.1Worst-Case 分析
什么情况会出现Worst-Case呢???
当输入的序列是有序的,或者逆序的时候,即划分的一边一直是没有元素。原因是,此时:
上面的解,可以用递归树来验证,从上到下的每一层分别是n,n-1,…,Theta(1),
4.2.2 Best-Case 分析
该分析只为直观理解,因为大多数的情况下,我们并不关心最好的情况,因为这根本无法保证算法的性能。第一种情况: 假设每一次划分都能把序列分成n/2:n/2,即每次都能将序列对半分,此时:T(n)= 2T(n/2) + Theta(n) = Theta(nlgn)这与归并排序的时间复杂度相同。第二种情况:假设每一次划分都是将序列的长度分为1/10:9/10,此时:T(n)= T(n/10) + T(9n/10) + Theta(n) = Theta(nlgn)具体的推导,请参照视频或者课件,这里给出按递归树推导的最后一步,如下图
上图中需要注意的是,每一个分支的深度是不同的,e.g.,一直按1/10展开的路径是log10(n),而一直按9/10展开的路径深度是log(10/9)(n),那么由此即可以得到T(n)的上下界,都为
T(n)= Theta(nlgn)
我们将Theta(n^2)的情况当做unlucky情况,而把Theta(nlgn)的情况当做lucky情况,一个更一般的假设是,这两种情况交替出现时结果是什么???
4.2.3 交替情况分析
Lucky情况时,时间复杂度为:
L(n)= 2U(n/2) + Theta(n)
Unlucky情况时,时间复杂度为:
U(n)= L(n-1) + Theta(n)
用代入法很容易解决,即:
L(n)= 2(L(n/2 - 1) + Theta(n/2) ) + Theta(n)
=2L(n/2 - 1) + Theta(n)
=Theta(nlgn) <—Lucky
即,在二者交替出现时,结果仍然是最好的情况,那么怎么样保证结果一直是好的呢???
随机快排法:
——序列随机化
——随机选择pivot
视频中给出了随机pivot的分析,运用了一点统计的知识,这里不再给出证明,其结果是证明了其运行时间的期望为:
E[T(n)]<= anlg(n),即其时间复杂度仍然是Theta(nlgn)