在编程珠玑一书对快速排序讲得较为透彻,最早的快排是单向的,慢慢演化成双向的,也就是目前的版本。从此书能看到这种演化的必要性。我想在这里,对其原理搞懂了就不会忘了(^_^)。
### 快速排序思想
1962年,由C.A.R.Hoare创造出来。该算法核心思想就一句话:“**排序数组时,将数组分成两个小部分,然后对它们递归排序**”。然而采取什么样的策略将数组分成两个部分是关键,想想看,如果随便将数组A分成A1和A2两个小部分,即便分别将A1和A2排好序,那么A1和A2重新组合成A时仍然是无序的。所以,我们可以在数组中找一个值,称为key值,我们在把数组A分解为A1和A2前先对A做一些处理,让小于key值的元素都移到其左边,所有大于key值的元素都移到其右边。这样递归排序A1和A2,数组A就排好了。
**举例** 我们要排序的数组如下:
| 55 | 41 | 59 | 26 | 53 | 58 | 97 | 93 |
|-----|-----|-----|-----|-----|-----|-----|-----|
我们选取第一个元素作为key值,即55.(一般都是选取第一个元素)。假如我们有一种办法可以将数组做一步预处理,让小于key值的元素都位于其左边,大于key值的元素都位于其右边,预处理完数组如下:
| 41 | 26 | 53 | 55 | 59 | 58 | 97 | 93 |
|-----|-----|-----|-----|-----|-----|-----|-----|
这样数组就被key值划分成了两段,A[0...2]小于key,A[4...7]大于key,可见key值本身已排好序,接下来对A[0...2]和A[4...7]分别进行递归排序,那么整个数组就排好序了。预处理做的工作再次澄清下:找一个key值,把key位放到某位置A[p],使小于key值的元素都位于A[p]左边,大于key值的元素都位于A[p]的右边。到此,我们的快排模型就成型了。
~~~
//s, u 代表待排序部分的下界和上界
void qsort(s, u){
//递归结束条件是待排序部分的元素个数小于2
if(s >= u) {
return;
}
//此处进行预处理,预处理后key值位于位置p
qsort(s, p-1);
qsort(p+1, u);
}
~~~
接下来看如何做预处理。我们选取A[0]做为key值, p作为key值的位置。我们从A[1]开始遍历后面的数组,用变量i指示目前的位置,每次找到小于key的值都与A[++p]交换。开始时p=0.
| 55 | 41 | 59 | 26 | 53 | 58 | 97 | 93 |
|-----|-----|-----|-----|-----|-----|-----|-----|
i = 1,A[i]位置为41, 即A[i] < key, swap(++p , i),即p = 1:
| 55 | 41 | 59 | 26 | 53 | 58 | 97 | 93 |
|-----|-----|-----|-----|-----|-----|-----|-----|
i = 2,A[i]位置为59,A[i] > key,不做任何改变。
i = 3,A[i]位置为26,A[i] < key,swap(++p, i), 即p = 2:
| 55 | 41 | 26 | 59 | 53 | 58 | 97 | 93 |
|-----|-----|-----|-----|-----|-----|-----|-----|
i = 4,A[i]位置为53,A[i] < key,swap(++p, i),p = 3:
| 55 | 41 | 26 | 53 | 59 | 58 | 97 | 93 |
|-----|-----|-----|-----|-----|-----|-----|-----|
i = 5,A[i]位置为58,A[i] > key,不做任何改变。
i = 6,A[i]位置为97,A[i] > key,不做任何改变.
i = 7,A[i]位置为93,A[i] > key,不做任何改变.结束循环。此时p为key的最终位置。还需一步把key值填入p位置。
最后swap(l, p)即把Key值放到最终位置上了。至于为什么要交换l,p的位置,可以另拿一组数据试一下:55,41,59,26,99,58,97,93。
~~~
//l, u 代表待排序部分的下界和上界
void qsort(int l, int u){
//递归结束条件是待排序部分的元素个数小于2
if(l >= u) return;
int p = l;
for(int i = l+1; i <= u; i++) {
if(A[i] < A[l])
swap(++p, i);
}
swap(l, p);
qsort(l, p-1);
qsort(p+1, u);
}
~~~
这就是第一代快速排序算法,正常情况下其复杂度为nlogn,但在考虑一种极端情况:n个相同元素组成的数组。在n-1次划分中每次划分都需要O(n)的时间,所以总的时间为O(n^2)。使用双向划分就可以避免这个问题。
### 双向划分快速排序
~~~
/*l, u 代表待排序部分的下界和上界*/
void qsort(int l, int u){
/*递归结束条件是待排序部分的元素个数小于2*/
if(l >= u) return;
key = A[l]
for(int i = l, j = u+1; i <= j; ) {
do i++ while(i <= u && A[i] < key));
do j-- while(A[j] > key);
if(i > j)
break;
swap(i, j);
}
swap(l, j);
qsort(l, j-1);
qsort(j+1, u);
}
~~~
**转载请注明了出处**:[http://blog.csdn.net/utimes/article/details/8762451](http://blog.csdn.net/utimes/article/details/8762451)
- 前言
- 螺旋矩阵、螺旋队列算法
- 程序算法艺术与实践:稀尔排序、冒泡排序和快速排序
- Josephu 问题:数组实现和链表实现
- 杨辉三角形算法
- 位图排序
- 堆排序的实现
- Juggling算法
- 【编程珠玑】排序与位向量
- 取样问题
- 变位词实现
- 随机顺序的随机整数
- 插入排序
- 二分搜索
- 产生不重复的随机数
- 约瑟夫环解法
- 快速排序
- 旋转交换或向量旋转
- 块变换(字符反转)
- 如何优化程序打印出小于100000的素数
- 基本的排序算法原理与实现
- 利用马尔可夫链生成随机文本
- 字典树,后缀树
- B-和B+树
- 程序算法艺术与实践引导
- 程序算法艺术与实践:基础知识之有关算法的基本概念
- 程序算法艺术与实践:经典排序算法之桶排序
- 程序算法艺术与实践:基础知识之函数的渐近的界
- 程序算法艺术与实践:递归策略之矩阵乘法问题
- 程序算法艺术与实践:递归策略之Fibonacci数列
- 程序算法艺术与实践:递归策略基本的思想
- 程序算法艺术与实践:经典排序算法之插入排序
- 程序算法艺术与实践:递归策略之递归,循环与迭代