主要内容 #
- 算法讲解
- 例程
- 复杂度分析
1. 算法讲解 #
快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。
快速排序又是一种分而治之思想在排序算法上的典型应用。本质上来看,快速排序应该算是在冒泡排序基础上的递归分治法。
快速排序的名字起的是简单粗暴,因为一听到这个名字你就知道它存在的意义,就是快,而且效率高!它是处理大数据最快的排序算法之一了。
1.1 计算步骤 #
一般来说,具体算法描述如下:
1. 挑选基准值:从数列中挑出一个元素,称为“基准”(pivot),
2. 分割:重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(与基准值相等的数可以到任何一边)。在这个分割结束之后,对基准值的排序就已经完成,
3. 递归排序子序列:递归地将小于基准值元素的子序列和大于基准值元素的子序列排序。
递归到最底部的判断条件是数列的大小是零或一,此时该数列显然已经有序。
1.2过程模拟 #
原始数据:int a[] = {49,38,65,97,76,13,27,49};
l---------------------------r // l 表示最左边的数的序号;r 表示最右边的数的序号
49 38 65 97 76 13 27 49
过程分析:
1. 找出数组中间的数,76
l---------------------------r
[ 49 38 65 97 (76) 13 27 49]
2. 从【左边】开始查找【第一个大于】 76 的数,用 i 记录,见 i97
l-----------i---------------r
[ 49 38 65 97 (76) 13 27 49]
3. 从【右边】开始查找【第一个小于】 76 的数,用 j 记录,见 j49
l-----------i---------------jr // jr 表示在这里就 j 和 r 相同
[ 49 38 65 97 (76) 13 27 49]
4. 判断是否要交换 i97 和 j49。【判断条件 1: a[i] > [j]】;【判断条件 2: i <= j】。
l-----------i---------------jr
[ 49 38 65 49 (76) 13 27 97] // 2 个条件都成立,所以交换 i97 和 j49
5. i++;j--;
l---------------i-------j---r
[ 49 38 65 49 (76) 13 27 97]
6. 继续步骤 2、3,找到 j27 i97,步骤 4 中的条件不成立,不交换
l-----------------------j---r
[ 49 38 65 49 (76) 13 27 97]
7. 循环条件【i<=j】不成立。分成 2 个数组,序号范围分别是 l~j,i~r
l-----------------------j---ir // ir 在这里相同,都是 97 的序号
[ 49 38 65 49 (76) 13 27 97]
|
\|/
[ 49 38 65 49 76 13 27][97]
8. 分别对两个数组,继续进行步骤 1-7 的操作。
8.1 先看左边的,取中间数 49
l------------------------r // 这里的 lr 要理解成递归调用中的l和r,表示数组范围
[ 49 38 65 (49) 76 13 27]
8.2 从【左边】开始查找【第一个大于】 49 的数,用 i 记录,见 i65
l-------i----------------r
[ 49 38 65 (49) 76 13 27]
8.3 从【右边】开始查找【第一个小于】 49 的数,用 j 记录,见 j27
l-------i----------------jr
[ 49 38 65 (49) 76 13 27]
8.4 判断是否要交换 i65 和 j27。【判断条件 1: a[i] > [j]】;【判断条件 2: i <= j】。
l-------i----------------jr
[ 49 38 27 (49) 76 13 65] // 2 个条件都成立,所以交换
8.5 i++;j--;
l-----------i--------j---r
[ 49 38 27 (49) 76 13 65]
8.6 继续步骤 2、3,找到 i76 和 j13
l----------------i---j---r
[ 49 38 27 (49) 76 13 65]
8.7 【判断条件 1: a[i] > [j]】;【判断条件 2: i <= j】
l----------------i---j---r
[ 49 38 27 (49) 13 76 65] // 2 个条件都成立,所以交换
8.8 i++;j--;
l----------------j---i---r
[ 49 38 27 (49) 13 76 65] // 2 个条件都成立,所以交换
8.9 循环条件【i<=j】不成立。分成 2 个数组,序号范围分别是 l~j,i~r
l----------------j---i---r
[ 49 38 27 49 13][76 65]
********** 请试着补全后续操作步骤 *******
最终:
[ 13 27 38 49 49 65 76 97 ]
动画演示如下图所示:

2. 例程 #
示例程序:
#include < iostream >
using namespace std;
int a[] = { 49, 38, 65, 97, 76, 13, 27, 49 };
void qsort(int l, int r)
{
int i = l;
int j = r;
int mid = a[(l + r) / 2];
do
{
while (a[i] < mid) i++;
while (a[j] > mid) j--;
if (i <= j)
{
swap(a[i], a[j]);
i++;
j--;
}
} while (i <= j);
if (l < j) qsort(l, j); // 递归
if (i < r) qsort(i, r);
}
int main()
{
qsort(0, 7);
for (int i=0; i < 8; ++i)
{
cout << a[i] << " ";
}
return 0;
}
3. 复杂度分析 #
在平均状况下,排序 n 个项目要 Ο(nlogn) 次比较。在最坏状况下则需要 Ο(n2) 次比较,但这种状况并不常见。事实上,快速排序通常明显比其他 Ο(nlogn) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。
习题 #
请默写上述代码。