1. 从数列中挑出一个元素,称为 "基准"(pivot),
2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
例如:设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用第一个数据)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序。值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。
一趟快速排序的算法是:
1)设置两个变量i、j,排序开始的时候:i=0,j=N-1;2)以第一个数组元素作为关键数据,赋值给key,即 key=A[0];
3)从j开始向前搜索,即由后开始向前搜索(j -- )直到i等于j就停止,找到第一个小于key的值A[j],A[i]与A[j]交换,i++, 因为A[i]已为小于key的数;
4)从i开始向后搜索,即由前开始向后搜索(i ++ )直到i等于j就停止,找到第一个大于key的A[i],A[i]与A[j]交换,j--,因为A[j]已为大于key的数;
5)重复第3、4步,直到 I=J。
也可以不使用key这个变量,直接进行交换,但这样代码不好理解,不够清晰,参见源码2
上边是随机化快排,即基准数key是随机选取的,还有其他的快速排序:平衡快排、外部快排、 三路基数快排
度量:
数据结构 不定
最差时间复杂度 O(n^2)
最优时间复杂度 O(nlog n)
平均时间复杂度 O(nlog n)
最差空间复杂度 根据实现的方式不同而不同
最佳算法 有时是
源码1:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
void print_ary( int * ary, unsigned int len )
{
while( len-- > 0 ) {
printf("%d ", *ary++ );
}
putchar('\n');
}
void create_rondom_number(int **parray, unsigned int number)
{
struct timeval tpstart;
unsigned int i =0;
if (NULL != *parray)
{
return;
}
*parray = (int *)malloc(sizeof(unsigned int) * number);
(void)memset(*parray, 0, sizeof(unsigned int) * number);
gettimeofday(&tpstart,NULL);
//srandom(time(NULL));
srandom(tpstart.tv_usec);
for (i=0; i<number; ++i)
{
//(*parray)[i] = random()%number; // range is [0, number-1]
//(*parray)[i] = random()%(b-a) + a; //range is [a, b)
((*parray)[i] = 1 + (int) ((double)number * rand() / (RAND_MAX + 1.0))); // [1, number]
}
}
void swap( int * a, int * b )
{
int temp = *a;
*a = *b;
*b = temp;
}
/*
*start 第一个有效的元素,即从0开始,end是最后一个有效的元素
*/
void quicksort(int a[],int start, int end)
{
int right ,left,key;
if (start>=end) {
printf("------------start =%d, end=%d\n", start, end);
return ;
}
left=start;
right=end;
key=a[left]; //选第一个元素为进行划分的数
a[left] = 10000;
printf("\n\n---left=%d right=%d key=%d: ", left, right, key);
print_ary( a, end - start + 1 );
while(left<right) //退出的条件是left与right相等,因为他们是一次减1,然后判断这两个值是否相等,所以最后他们会相等的
{
printf("\nfrom right=a[%d] to left=a[%d]=>", right, left);
while(left<right&&a[right]>key) { //从右边开始,挨个元素与key进行比较。a[left]是可以放置元素的位置, 因为它的值已经被挪走
right--;
}
if (left<right) {
a[left]=a[right];
a[right] = 10000;
printf("move a[%d] to a[%d]: ", right, left);
left++;
}
print_ary( a, end - start + 1 );
printf("\nfrom left=a[%d] to right=a[%d]=>", left, right);
while (left<right&&a[left]<key) {//从左边开始,挨个元素与key进行比较。a[right]是可以放置元素的位置, 因为它的值已经被挪走
left++;
}
if (left<right) {
a[right]=a[left];
a[left] = 10000;
printf("move a[%d] to a[%d]: ", left, right);
right--;
}
print_ary( a, end - start + 1 );
}
a[left]=key; //此时right和left的值已经相等
printf("\n\nfinish partition left=%d right=%d key=%d: ", left, right, key);
print_ary( a, end - start + 1 );
/*a[left/right]已经在正确的位置了,不需要再排序了*/
#if 0
if (start<left-1) {//左边至少要有两个元素才往下继续
quicksort(a,start,left-1);
}
if (left+1<end){//左边至少要有两个元素才往下继续
quicksort(a,left+1,end);
}
#endif
/*下边这种方式虽然导致多进入了2个函数递归,但更能体现递归的思路,表达更清晰*/
quicksort(a,start,left-1); //left start end必须是有符号数,因为left - 1 会出现负数
quicksort(a,left+1,end);
}
int main( int argc, char ** argv )
{
int *pary = NULL;
if (argc !=2)
{
printf("\nUsage: %s Number\n", argv[0]);
return -1;
}
create_rondom_number(&pary, (unsigned int)atoi(argv[1]));
printf("\n\nrandom number: ");
print_ary( pary, (unsigned int)atoi(argv[1]) );
printf("Start sortting: \n");
quicksort( pary, 0, (unsigned int)atoi(argv[1]) - 1);
printf("\n\nafter sorted: ");
print_ary( pary, (unsigned int)atoi(argv[1]) );
return 0;
}
运行结果:
[root@localhost sort]# ./qsort1 10
random number: 4 9 3 5 9 5 7 4 4 6
Start sortting:
---left=0 right=9 key=4: 10000 9 3 5 9 5 7 4 4 6
from right=a[9] to left=a[0]=>move a[8] to a[0]: 4 9 3 5 9 5 7 4 10000 6
from left=a[1] to right=a[8]=>move a[1] to a[8]: 4 10000 3 5 9 5 7 4 9 6
from right=a[7] to left=a[1]=>move a[7] to a[1]: 4 4 3 5 9 5 7 10000 9 6
from left=a[2] to right=a[7]=>move a[3] to a[7]: 4 4 3 10000 9 5 7 5 9 6
from right=a[6] to left=a[3]=>4 4 3 10000 9 5 7 5 9 6
from left=a[3] to right=a[3]=>4 4 3 10000 9 5 7 5 9 6
finish partition left=3 right=3 key=4: 4 4 3 4 9 5 7 5 9 6
---left=0 right=2 key=4: 10000 4 3
from right=a[2] to left=a[0]=>move a[2] to a[0]: 3 4 10000
from left=a[1] to right=a[2]=>move a[1] to a[2]: 3 10000 4
finish partition left=1 right=1 key=4: 3 4 4
------------start =0, end=0
------------start =2, end=2
---left=4 right=9 key=9: 3 4 4 4 10000 5
from right=a[9] to left=a[4]=>move a[9] to a[4]: 3 4 4 4 6 5
from left=a[5] to right=a[9]=>move a[8] to a[9]: 3 4 4 4 6 5
finish partition left=8 right=8 key=9: 3 4 4 4 6 5
---left=4 right=7 key=6: 3 4 4 4
from right=a[7] to left=a[4]=>move a[7] to a[4]: 3 4 4 4
from left=a[5] to right=a[7]=>move a[6] to a[7]: 3 4 4 4
finish partition left=6 right=6 key=6: 3 4 4 4
---left=4 right=5 key=5: 3 4
from right=a[5] to left=a[4]=>move a[5] to a[4]: 3 4
from left=a[5] to right=a[5]=>3 4
finish partition left=5 right=5 key=5: 3 4
------------start =4, end=4
------------start =6, end=5
------------start =7, end=7
------------start =9, end=9
after sorted: 3 4 4 4 5 5 6 7 9 9
[root@localhost sort]#
源码2:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
void print_ary( int * ary, unsigned int len )
{
while( len-- > 0 ) {
printf("%d ", *ary++ );
}
putchar('\n');
}
void create_rondom_number(int **parray, unsigned int number)
{
struct timeval tpstart;
unsigned int i =0;
if (NULL != *parray)
{
return;
}
*parray = (int *)malloc(sizeof(unsigned int) * number);
(void)memset(*parray, 0, sizeof(unsigned int) * number);
gettimeofday(&tpstart,NULL);
//srandom(time(NULL));
srandom(tpstart.tv_usec);
for (i=0; i<number; ++i)
{
//(*parray)[i] = random()%number; // range is [0, number-1]
//(*parray)[i] = random()%(b-a) + a; //range is [a, b)
((*parray)[i] = 1 + (int) ((double)number * rand() / (RAND_MAX + 1.0))); // [1, number]
}
}
void swap( int * a, int * b )
{
int temp = *a;
*a = *b;
*b = temp;
}
void quick_sort( int * ary, int len )
{
int left, right;
if( len <= 1 )
return;
left = 0;
right = len - 1;
while( 1 ) {
while( left != right && ary[right] > ary[left] ) { //基准数位为ary[left],第一次进来的时候,基准数是ary[0],即第一个元素
right --;
}
if( left == right ) {
break;
}
swap( ary+left, ary+right );//与基准数ary[left]进行交换,此时基准数位ary[right]
left ++;
while( left != right && ary[left] < ary[right] ) { //基准数位为ary[right]
left ++;
}
if( left == right ) {
break;
}
swap( ary+left, ary+right );//与基准数ary[right]进行交换,此时基准数位ary[left]
}
#if 0
if( left == 0 ) {
quick_sort( ary, left+1 );
quick_sort( ary+left+1, len-left-1 );
} else {
quick_sort( ary, left );
quick_sort( ary+left, len-left );
}
#endif
quick_sort(ary, left);
quick_sort(ary+left+1, len - left - 1);
}
int main( int argc, char ** argv )
{
int *pary = NULL;
if (argc !=2)
{
printf("\nUsage: %s Number\n", argv[0]);
return -1;
}
create_rondom_number(&pary, (unsigned int)atoi(argv[1]));
printf("\n\nrandom number: ");
print_ary( pary, (unsigned int)atoi(argv[1]) );
printf("Start sortting: \n");
quick_sort( pary, (unsigned int)atoi(argv[1]));
printf("\n\nafter sorted: ");
print_ary( pary, (unsigned int)atoi(argv[1]) );
return 0;
}
运行结果:
[root@localhost sort]# ./qsort 100
random number: 50 98 70 37 48 34 19 92 7 65 86 41 68 19 10 14 61 85 27 32 24 49 62 21 3 80 56 84 34 9 55 84 6 25 21 53 58 39 45 65 3 30 5 71 48 14 84 9 98 11 40 22 60 2 42 62 81 97 45 15 5 99 99 11 24 20 64 81 58 8 45 61 38 50 31 85 63 15 93 61 25 33 82 84 34 24 46 15 21 90 30 26 89 29 36 12 48 100 93 5
Start sortting:
after sorted: 2 3 3 5 5 5 6 7 8 9 9 10 11 11 12 14 14 15 15 15 19 19 20 21 21 21 22 24 24 24 25 25 26 27 29 30 30 31 32 33 34 34 34 36 37 38 39 40 41 42 45 45 45 46 48 48 48 49 50 50 53 55 56 58 58 60 61 61 61 62 62 63 64 65 65 68 70 71 80 81 81 82 84 84 84 84 85 85 86 89 90 92 93 93 97 98 98 99 99 100
[root@localhost sort]#
参考:
http://zh.wikipedia.org/wiki/%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F
http://baike.baidu.com/view/115472.htm#3_12