快速排序算法分析
分析与步骤
快速排序算法和归并排序类似,都是属于分治算法。
快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。
步骤为:
从数列中挑出一个元素,称为 “基准”(pivot),
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
虽然快速排序是分治算法,但其中主要的操作是分区操作,这个操作的复杂性应该是线性的。划分的实现由很多种,朴素的实现是选取第一个元素和最后一个元素作为基准作为划分。算法导论一书给出的partition 函数是取最后一个元素作为基准,然后通过一遍循环交换元素。代码虽然简洁,但是初看起来不太易懂,建议在纸上模拟下过程。此种方法相对来说交换元素的次数也比较多,后面会给出一种优化的操作。
C语言实现
/* 快速排序朴素的实现 */
#include<stdio.h>
void swap(int* a, int* b)
{
int t = *a;
*a = *b;
*b = t;
}
/*取最后一个作为基准 */
int partition (int arr[], int l, int h)
{
int x = arr[h];
int i = (l - 1); // 较小元素的下标
for (int j = l; j <= h- 1; j++)
{
if (arr[j] <= x)
{
i++; //增加较小元素的下标
swap(&arr[i], &arr[j]);
}
}
swap(&arr[i + 1], &arr[h]);
return (i + 1);//返回基准元素的最终位置
}
/* arr[] --> 待排序数组, l --> 开始位置, h -->结束位置 */
void quickSort(int arr[], int l, int h)
{
if (l < h)
{
int p = partition(arr, l, h); /* p为划分的中间位置 */
quickSort(arr, l, p - 1);
quickSort(arr, p + 1, h);
}
}
/* 打印数组 */
void printArray(int arr[], int size)
{
int i;
for (i=0; i < size; i++)
printf("%d ", arr[i]);
printf("\n");
}
// 测试
int main()
{
int arr[] = {10, 7, 8, 9, 1, 5};
int n = sizeof(arr)/sizeof(arr[0]);
quickSort(arr, 0, n-1);
printf("Sorted array: \n");
printArray(arr, n);
return 0;
}
输出:
Sorted array:
1 5 7 8 9 10
时间复杂度可以算出为时间的复杂度可以表示为:T(n) = T(k) + T(n-k-1) + P(n)
虽然快速排序的最坏情况下复杂度为O(N^2),这比很多其他的排序算法,如归并排序和堆排序,在实践中更快.