【算法导论】快速排序
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]已经有序。
下面的程序实现快速排序:
数组的划分:
算法的关键部分是PARTITION过程,它实现了对子数组A[p..r]的原址重排。
PARTITION方案一
快速排序的随机化版本只对PARTITION和QUICKSORT的代码改动非常少。在新的划分程序中,我们只是在真正进行划分前进行一次交换:
一种改进的RANDOMIZED-QUICKSORT的方法是在划分时,要从子数组中更细致地选择作为主元的元素(而不是简单的随机选择),常用的做法是三数取中法:从子数组中随机选出三个元素,取其中位数作为主元(算法从略)。
新的快速排序不再调用PARTITION,而是调用RANDOMIZED-PARTITION:
选择第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小的元素。
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小的元素。
对于包含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+1PARTITION方案二
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 j2. 快速排序的随机化版本
快速排序的随机化版本只对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小的元素。
相关文章推荐
- Highmaps网页图表教程之绘图区显示标签显示数据标签定位
- Qt 中显示中文
- 直方图规定化
- C++指针加整数、两个指针相减的问题
- EXPLAIN sql优化方法2 Using temporary ; Using filesort
- 修改arm开发板的IP地址
- 快速排序原理
- zoj2112
- PHP查询数据库中满足条件的记录条数(二种实现方法)
- ORA-00904: "STUDENTNUMBER": 标识符无效
- javascript--QUnit【javascript单元测试框架】
- HTTP头字段总结
- NOI系列赛中 C++容器使用总结
- contourArea函数
- HDU 1171 Big Event in HDU (由01背包演变的水题)
- UNIX网络编程0 IP头,TCP头,UDP头,MAC帧头定义
- app上传 需要的icon
- ecshop 分页
- UVA-1347 Tour
- C语言判断字符是否为可打印字符的方法