排序算法之快速排序

算法描述:

    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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值