基础
看懂《算法导论》需要重温数学知识。算法实现和推导是一个数学建模过程。
原理
对于包含 n 个数的输入数组来说,快速排序是一种最坏情况时间复杂度为 O() 的排序算法。虽然最坏情况的时间复杂度很差,但是快排通常是实际排序应用中最好的选择,因为它平均性能非常好,它的期望实际复杂度是O(),而且O()中隐含的常数因子非常小,另外它还能够进行原址排序,甚至在虚存环境中也能很好地工作。
详细内容请参考《算法导论》第三版,第二部分,第七章:快速排序
实现
根据数组哨兵的选择,有两种递归方式实现。调试代码在 (github)
- 以数组末位数值为哨兵,从左向右排序
int Partition(int array[], int start, int end) {
int low = start - 1;
int high = low + 1;
int key = array[end];
for (; high < end; high++) {
if (array[high] <= key) {
low++;
if (high > low) {
int temp = array[low];
array[low] = array[high];
array[high] = temp;
}
}
}
// 如果是有序数组,会出现左边都是最小的情况,要置换 partition 需要判断数据。
int partition = low + 1;
if (array[partition] > key) {
int temp = array[partition];
array[partition] = array[end];
array[end] = temp;
}
return partition;
}
void qsort_end(int array[], int start, int end) {
if (start < 0 || end <=0 || start >= end) {
return;
}
int partition = Partition(array, start, end);
if (partition >= 0) {
qsort_end(array, start, partition - 1);
qsort_end(array, partition + 1, end);
}
}
- 以数组中间数值为哨兵,从两端向中间排序
void qsort_mid(int array[], int start, int end) {
if (start >= end) {
return;
}
int high = end;
int low = start;
int key = array[(unsigned int)(start + end) / 2];
while (low < high) {
// 左边向右查找比 key 大的
while (array[low] < key && low < end) {
low++;
}
// 右边向左查找比 key 小的
while (array[high] > key && high > start) {
high--;
}
if (low <= high) {
int temp = array[low];
array[low] = array[high];
array[high] = temp;
low++;
high--;
}
}
qsort_mid(array, start, high);
qsort_mid(array, low, end);
}
时间复杂度推导
最优情况下的时间复杂度
快速排序涉及到递归调用, 递归算法的时间复杂度公式:
数组共有 个数值,最优的情况是每次取到的元素(哨兵)刚好平分整个数组。
此时的时间复杂度公式为:
第一次递归:
第二次递归:令 ,
第三次递归:令
…
第 次递归:令
公式一直往下迭代,当最后数组不能再平分时,最后到,说明公式迭代完成(是常量)也就是:
==> ( ) ==> ( )
当 时
为元素个数,当 时
参考
算法导论 时间复杂度分析
快速排序 及其时间复杂度和空间复杂度
算法导论------递归算法的时间复杂度求解
算法复杂度中的O(logN)底数是多少
Cmd Markdown 公式指导手册