一、快速排序算法的基本特性
时间复杂度:O(n*lgn)
最坏:O(n^2)
空间复杂度:O(n*lgn)
不稳定。
快速排序是一种排序算法,对包含n个数的输入数组,平均时间为O(nlgn),最坏情况是O(n^2)。
通常是用于排序的最佳选择。因为,排序最快,也只能达到O(nlgn)。
二、快速排序算法的描述
算法导论,第7章
快速排序时基于分治模式处理的,
对一个典型子数组A[p...r]排序的分治过程为三个步骤:
1.分解:
A[p..r]被划分为俩个(可能空)的子数组A[p ..q-1]和A[q+1 ..r],使得
A[p ..q-1] <= A[q] <= A[q+1 ..r]
2.解决:通过递归调用快速排序,对子数组A[p ..q-1]和A[q+1 ..r]排序。
3.合并。
三、快速排序算法
快速排序算法的关键是PARTITION过程,它对A[p..r]进行就地重排:
PARTITION(A, p, r)
1 x ← A[r] //以最后一个元素,A[r]为主元
2 i ← p - 1
3 for j ← p to r - 1 //注,j从p指向的是r-1,不是r。
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
然后,对整个数组进行递归排序:
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)
#include <stdio.h>
void swap(int *a, int *b);
void print(int* data, int len);
int partition(int* data, int lo, int hi)
{
int i = lo - 1;
int j = lo;
int key = data[hi];
for (; j < hi; j++) // 住:j < hi ,不是 j <= hi
{
if (data[j] <= key)
{
i += 1;
swap(&data[i], &data[j]);
}
}
swap(&data[i+1], &data[hi]);
return i + 1;
}
void quicksort(int data[], int lo, int hi)
{
if (lo < hi)
{
int q = partition(data, lo, hi);
quicksort(data, lo, q-1);
quicksort(data, q+1, hi);
}
return;
}
void main()
{
int arr[] = {5, 4, 8, 9, 1, 3, 2, 6, 7, 10, 6};
print(arr, 11);
quicksort(arr, 0, 10);
print(arr, 11);
}
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
void print(int* data, int len)
{
for (int i = 0; i < len; i++)
{
printf("%4d",data[i]);
}
printf("\n");
}
五、拓展
问题描述:
我们将乱序的红白蓝三色小球排列成有序的红白蓝三色的同颜色在一起的小球组。这个问题之所以叫荷兰国旗,是因为我们可以将红白蓝三色小球想象成条状物,有序排列后正好组成荷兰国旗。如下图所示:
这个问题,类似快排中partition过程。不过,要用三个指针,一前begin,一中current,一后end,俩俩交换。
1、current遍历,整个数组序列,current++,
2、current指0,与begin交换,而后current++,begin++,
3、current指2,与end交换,而后,current不动,end--。
为什么,第三步,current指2,与end交换之后,current不动了列,对的,正如algorithm__所说:current之所以与begin交换后,current++、begin++,是因为此无后顾之忧。而current与end交换后,current不动,end--,是因有后顾之忧。
为什么啊,因为你想想啊,你最终的目的无非就是为了让0、1、2有序排列,试想,如果第三步,current与end交换之前,万一end之前指的是0,而current交换之后,current此刻指的是0了,此时,current能动么?不能动啊,指的是0,还得与begin交换列。
ok,说这么多,你可能不甚明了,直接引用下gnuhpc的图,就一目了然了:
本程序代码:
#include <stdio.h>
void swap(int *a, int *b);
void print(int* data, int len);
void main()
{
int arr[] = {0, 1, 2, 1, 1, 2, 0, 2, 1, 0};
/* 初始化 begin, current, end */
int *begin = arr;
int *current = arr;
int *end = arr + 9;
/* 排序前,打印数组 */
print(arr, 10);
/* current 遍历整个数组 */
while (current <= end)
{
/* current指0,与begin交换,而后current++,begin++ */
if (0 == *current)
{
swap(begin, current);
begin++;
current++;
}
/* current指向1,current ++ */
else if (1 == * current)
{
current++;
}
/* current指2,与end交换,而后,current不动,end-- */
else
{
swap(end, current);
end--;
}
}
/* 排序后,打印数组 */
print(arr, 10);
}
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
void print(int* data, int len)
{
for (int i = 0; i < len; i++)
{
printf("%4d",data[i]);
}
printf("\n");
}