快速排序简介
对于包含n个数的输入数组来说,虽然快速排序是一种最坏情况实际复杂度为
Θ(n2)
的排序算法, 但它的平均性能非常好: (1)它的期望时间复杂度是
Θ(nlgn)
,而且 (2)
Θ(nlgn)
中隐含的常数因子非常小. (3) 它还能进行原址排序.
快排目前公认的排序算法里面实际应用中最快的最好的最常用的排序算法.
快速排序的描述
快速排序与归并排序一样也是使用了分治思想. 下面是对一个典型例子的数组A[p..r]进行快速排序的三步分治过程:
分解: 数组 A[p..r] 被划分为两个(可能为空)子数组 A[p..q-1] 和 A[q+1..r],使得 A[p..q-1] 中的每一个元素都小于等于 A[q], 而 A[q] 也小于等于 A[q+1..r] 中的每一个元素。
解决: 通过递归调用快速排序,对子数组 A[p..q-1] 和 A[q+1..r] 进行排序
合并: 因为子数组都是原址排序的,所以不需要合并操作:数组 A[p..r] 已经有序。
QUICKSORT(A, p, r)
if p < r
q = PARTITION(A, p, r)
QUICKSORT(A, p, q-1)
QUICKSORT(A, q+1, r)
初始调用QUICKSORT(A, 1, A.length)
算法的关键部分是PARTITON过程, 它实现了对子数组 A[p..r] 的原址重排.
PARTITION(A, p, r)
x = A[r]
i = p -1
for j = p to r - 1
if A[j] <= x
i = i + 1
exchange A[i] with A[j]
exchange A[i+1] with A[r]
return i+1
下面是一个样例数组上的PARTITION操作过程:
在子数组 A[p..r] 上, PARTITION 维护了 4 个区域。 A[p..i] 区间的所有值都小于等于 x, A[i+1..j-1]区间内的所有值都大于x, A[r]=x.子数组 A[j..r-1] 中的值可能属于任何一种情况.如下图:
所以更标准化的PARTITION过程如下:
快速排序的随机化版本
当划分产生的两个子问题分别包含 n -1 个元素 和 0 个元素 时, 快速排序的最坏情况发生了.
那么怎么防止最坏情况的发生呢,那就是随机抽样(random sampling),与始终采用A[r]作为主元的方法不同,随机抽样是从子数组A[p..r]中随机选择一个元素作为主元。然后与A[r]进行元素交换。、
那么随机化版本快排如下:
RANDOMIZED-PARTITION(A, p, r)
i = RANDOM(p, r)
exchange A[r] with A[i]
return PARTITION(A, p, r)
RANDOMIZED-QUICKSORT(A, p, r)
if p < r
q = RANDOMIZED-PARTITION(A, p, r)
RANDOMIZED-QUICKSORT(A, p, q-1)
RANDOMIZED-QUICKSORT(A, q+1, r)
(可以随机选数组中的3个元素(当数组个数多于2个的时候)中的元素, 如
A≤B≤C
,然后选取 B 作为主元(pivot element).
因为随机选取也需要花费一定时间,可以选取第一个元素,中间元素,和最后一个元素,然后选取它们中值大小为 mid 的作为 主元.)
C语言代码实现快排实例
#include <stdio.h>
void quick_sort(int *a, int p, int r);
int partition(int *a, int p, int r);
void print_array(int *a, int length);
int main(void)
{
int a[8] = {2, 8, 7, 5, 1, 6, 4, 3};
print_array(a, 8);
quick_sort(a, 0, 7);
printf("after quick sort\n");
print_array(a, 8);
return 0;
}
void print_array(int *a, int length)
{
int i = 0;
if (NULL == a || length <= 0){
printf("param error\n");
return;
}
for (i=0; i<length; ++i){
printf("%d\t", a[i]);
if (0 == (i%10) && i != 0)
printf("\n");
}
printf("\n");
}
void quick_sort(int *a, int p, int r)
{
int q = 0;
if (NULL == a || r < 0 || p < 0){
printf("[%s][%d]param is error, p=%d, r=%d\n",__FUNCTION__, __LINE__, p, r);
return;
}
if (p < r){
q = partition(a, p, r);
quick_sort(a, p, q-1);
quick_sort(a, q+1, r);
}
}
int partition(int *a, int p, int r)
{
int x = 0;
int i = 0;
int j = 0;
int tmp = 0;
x = a[r];
i = p - 1;
for (j = p; j <= r - 1; ++j){
if (a[j] <= x){
++i;
tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
}
// exchang a[i+1] with a[r]
tmp = a[i+1];
a[i+1] = a[r];
a[r] = tmp;
return i+1;
}