学习《算法导论》第七章 快速排序 总结
快速排序概论
快速排序通常是用于排序的最佳选择, 这是因为它的平均性能非常好, 期望的运行时间为O(nlg n),且常数因子很小. 另外, 它还能够进行就地排序. 它的最坏运行时间为O(n^2).
快速排序的思想
快速排序和合并排序一样, 也采用了分治的思想. 分治, 之前学过, 有三个步骤:分解, 解决, 合并. 下面对A[p...r]讲述快速排序的思想:
分解:A[p...r]被划分为两个子数组A[p...q-1]和A[q+1...r], 使得A[p...q-1]中的每个元素都小于等于A[q]和[q+1...r]中的每个元素. 其中q如何取值是这个算法的关键.
解决:递归解决两个子数组.
合并:因为这两个数组是就地排序的, 且大小关系已定. 合并不需要操作.
快速排序的算法
该算法最主要的地方就是数组的划分即q值的选择.
QUICKSORT(A, p, r)
1 if p < r
2 then q <--- PARTITION(A, p, r)
3 QUICKSORT(A, p, q - 1)
4 QUICKSORT(A, q + 1, r)
// 下面的PARTITION是就地排序, 是快速排序算法的关键.
PARTITION(A, p, r)
1 x <--- A[r] // x称为主元
2 i <--- p - 1
3 for j <--- p to r - 1
4 do if A[j] <= x
5 then i <--- i + 1
6 exchange A[i] <-> A[j]
7 exchange A[i + 1] <-> A[r]
8 return i + 1
注意:划分的过程时间复杂度为:O(n)
PARTITION(A, p, r)过程的作用就是将A[p…r]分为四个区域. A[p…i] 中的各个值都小于等于x. A[i + 1…j + 1]中的各个值都大于x. A[r] = x. 其他为任意值.
快速排序的程序实现
// 划分过程
int Partition(int* Array, int first, int last)
{
// PartitionValue为主元
int PartitionValue = Array[last];
int pLessValuePart = first - 1; // 注意:当first为0时, first - 1则反转. 所以这里要用int
int pMoreValuePart = 0;
for (pMoreValuePart = first; pMoreValuePart < last; pMoreValuePart++)
{
// 这部分难以理解, 可以画图来帮助理解
// 当Array[pMoreValuePart]的值小于主元时, 要将他的值和Array[pLessValuePart + 1]互换
// 这样才能确保pLessValuePart左边的值小于主元, pLessValuePart和pMoreValuePart之间的值是大于主元. 以达到划分的目的
if (Array[pMoreValuePart] <= PartitionValue)
{
pLessValuePart++;
swap (Array + pLessValuePart, Array + pMoreValuePart);
}
}
// 注意:Array + pLessValuePart + 1, Array + last都是大于pLessValuePart左边的值.
// 将这两个数互换, 因为Array[last]是主元, 划分过程就是按照主元来划分的, 所以要互换
swap (Array + pLessValuePart + 1, Array + last);
return pLessValuePart + 1;
}
// 快速排序
void QuickSort(int* Array, int begin, int end)
{
if (begin < end)
{
int pPartitionValue = Partition(Array, begin, end);
QuickSort(Array, begin, pPartitionValue - 1);
QuickSort(Array, pPartitionValue + 1, end);
}
return;
}