您的位置:首页 > 其它

【算法导论】快速排序

2015-08-26 10:36 323 查看
既然敢叫“快速排序”,必然有其过人之处。事实上,它确实是最快的通用内部排序算法。它由Hoare于1962年提出,相对归并排序来说不仅速度快,并且不需要辅助空间。
对于包含n个数的输入数组来说,快速排序是一种最坏情况时间复杂度为O(n^2)的排序算法。虽然最坏情况时间复杂度差,但是快速排序通常是实际排序应用中最好的选择,因为它的平均性能非常好:它的期望时间复杂度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]也小于等于A[q+1..r]中的每一个元素。其中,计算下标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)。

数组的划分:
算法的关键部分是PARTITION过程,它实现了对子数组A[p..r]的原址重排。
PARTITION方案一

PARTITION(A, p, r)
1. x = A[r]
2. i = p - 1
3. for j = p to r-1
4.     if A[j] <= x
5.         i = i+1
6.         exchange A[i] with A[j]
7. exchange A[i+1] with A[r]
8. return i+1
PARTITION方案二

HOARE-PARTITION(A, p, r)
1. x = A[p]
2. i = p-1
3. j = r+1
4. while TRUE
5.     repeat
6.         j = j-1
7.     until A[j] <= x
8.     repeat
9.         i = i+1
10.     until A[j] >= x
11.     if i < j
12.         exchange A[i] with A[j]
13.     else return j
2. 快速排序的随机化版本
快速排序的随机化版本只对PARTITION和QUICKSORT的代码改动非常少。在新的划分程序中,我们只是在真正进行划分前进行一次交换:
RANDOMIZED-PARTITION(A, p, r)
1. i = RANDOM(p, r)
2. exchange A[r] with A[i]
3. return PARTITION(A, p, r)


一种改进的RANDOMIZED-QUICKSORT的方法是在划分时,要从子数组中更细致地选择作为主元的元素(而不是简单的随机选择),常用的做法是三数取中法:从子数组中随机选出三个元素,取其中位数作为主元(算法从略)。
新的快速排序不再调用PARTITION,而是调用RANDOMIZED-PARTITION:

RANDOMIZED-QUICKSORT(A, p, r)
1. if p < r
2.     q = RANDOMIZED-PARTITION(A, p, r)
3.     RANDOMIZED-QUICKSORT(A, p, q-1)
4.     RANDOMIZED-QUICKSORT(A, q+1, r)
3. 选择问题

选择第k大的数,问题描述:

输入:一个包含n个(互异的)数的集合A和一个整数i, 1 <= i <= n。

输出:元素x属于A,且A中恰好有i-1个其他元素小于它。

4. 期望为线性时间的选择算法

一般选择问题看起来要比找最小值这样的简单问题更难,但令人惊奇的是,这两个问题的渐近运行时间却是相同的:O(n)。与快速排序一样,我们仍然对数组进行递归划分。但不同的是,快速排序会递归处理划分的两边,而RANDOMIZED-SELECT只处理划分的一边。
RANDOMIZED-SELECT利用了RANDOMIZED-PARTITION过程。与RANDOMIZED-QUICKSORT一样,因为它的部分行为是由随机数生成器的输出决定的,所以RANDOMIZE-SELECT也是一个随机算法。以下是RANDOMIZED-SELECT的伪代码,它返回数组A[p..r]中第i小的元素。
RANDOMIZED-SELECT(A, p, r, i)
1. if p == r
2.     return A[p]
3. q = RANDOMIZED-PARTITION(A, p, r)
4. k = q-p+1
5. if i == k
6.     return A[q]
7. else if i < k
8.     return RANDOMIZED-SELECT(A, p, q-1, i)
9. else return RANDOMIZE
4000
D-SELECT(A, q+1, r, i-k)

5. 最坏情况为线性时间的选择算法
通过执行以下步骤,算法SELECT可以确定一个有n>1个不同元素的输入数组中第i小的元素。(如果n=1,则SELECT只返回它的唯一输入数值作为第i小的元素。)

(1). 将输入数组的n个元素划分为n/5组,每组5个元素,且至多只有一组由剩下的n mod 5个元素组成;

(2). 寻找这n/5组中每一组的中位数:首先对每组元素进行插入排序,然后确定每组有序元素的中位数;

(3). 对第2步中找出的n/5个中位数,递用SELECT以找出其中位数x(如果有偶数个中位数,为了方便,约定x为较小的中位数); 

(4). 利用修改过的PARTITION版本,按中位数的中位数x对输入数组进行划分。让k比划分的低区中的元素数目多1,因此x是第k小的元素,并且有n-k个元素在划分的高区;

(5). 如果i=k,则返回x。如果i<k,则在低区递归调用SELECT来找出第i小的元素。如果i>k,则在高区递归查找第i-k小的元素。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: