您的位置:首页 > 其它

算法导论学习笔记 第7章 快速排序

2015-05-26 16:48 260 查看
对于包含n个数的输入数组来说,快速排序是一种时间复杂度为O(n^2)的排序算法。虽然最环情况的复杂度高,但是快速排序通常是实际应用排序中最好的选择,因为快排的平均性能非常好:它的期望复杂度是O(nlgn),而且O(nlgn)中的常数因子非常小。另外,快速排序还可以实现原址排序,甚至在虚拟环境中也能很好的工作。
1 快速排序的描述

与归并排序一样,快速排序也使用了分治法的思想,下面是对一个典型的子数组A[p.. r]进行快速排序的分治过长:

分解:数组A[p.. r]被划分为两个(可能为空)子数组A[p.. q-1]和A[q+1.. r],使得A[p.. q-1]中的每一个元素都小于A[q],而A[q+1..
r]中的每个元素都大于 A[q]。q也是划分过程的一部分。
解决:通过递归调用快速排序,对子数组A[p..
q-1]和A[q+1.. r]进行排序

合并:因为子数组都是原址排序的,所以不需要合并,数组A[p..
r]已经排序。

下面是快速排序的为代码:

QUICKSORT(A, p, r)
1 if p<r
2    q = PARTITION(A, p, r)
3    QUICKSORT(A, p, q-1)
4    QUICKSORT(A, q+1, r)
为了排序一个数组A的全部元素,初始调用QUICKSORT(A, 1, A.length)。

2 数组的划分

算法的关键部分是PARTION过程,他实现了对子数组A[p.. r]的原址排序。PARTION的伪代码表示如下:

PARTION(A, p, r)
1     x = A[r]
2     i = p - 1
3     for j=p to r-1
4          if A[u]<=x
5              i = i +1
6              exchange A[i] with A[j]
7     exchange A[i+1] with A[r]
8     return i + 1
下图表示了PARTION如何在一个包含8个元素的数组上进行操作的过程。PRATION总是选择一个x=A[r]作为主元(pivot element),并围绕它来划分数组A[p.. r] 。



PRTION在子数组A[p.. r]上的实际复杂度是O(n),其中n = r - p +1。

3 快速排素的性能

快速排序的运行时间依赖于划分是否平衡,而平衡与否又依赖于划分的元素。如果划分是平衡的,那么快速排序算法性能与归并排序一样,如果划分是不平衡的,那么快速排序的性能就接近与插入排序了,下面给出了快速排序性能的非形式化的分形:

最坏情况的划分:

  当划分产生的两个子问题分别包含了n-1个元素和0个元素是,快速排序的最坏情况发生了。不妨假设算法的每一次递归调用都出现了这种不平衡的划分。划分操作的时间复杂度是O(n)。由于对一个大小为0的数组递归调用谁直接返回,因此,T(0)=O(1),于是算法运行时间的递归式可以表示为:

T(n) = T(n-1) + T(0) + O(n) = T(n-1) + O(n)
  利用带入法可以直接得到递归式的解为T(n) = O(n^2)。因此,如果在算法的每一层递归上,划分都是最大程度不平衡,那么算法的时间复杂度为O(n^2)。

最好情况的划分

  在可能的最平衡的划分中,PARTION得到的两个字问题的规模都不大于n/2。这是因为一个子问题的规是n/2,而另一个字问题的规模为n/2 - 1。此种情况下,快速排序的性能非常好。此时,算法的运行时间的递归式为:

T(n) = T(n/2)  + O(n)

由主定理可知,上述递归式的解为O(nlgn)。
平衡的划分

快速排序的平均运行时间更接近于其最好情况,而非最坏情况。假设的算法总是产生9:1的划分,乍一看,这种划分是很不平衡的。这时候得到的快速排序的时间复杂度的递归为:

T(n) = T(9n/10) + T(n/10) + cn

书中采用了递归树的方式求出了上述递归的解为O(nlgn)。而且指出,只要划分是常数比例的,算法的运行时间总是O(nlgn)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  算法导论