• 分治算法三:快速排序


    一、算法思想

    1、利用渐增型算法分析中的划分序列算法,针对整个序列逐步划分,每次划分会确定分界点的下标,并将原问题分解成两个小规模问题;
    利用递归处理,继续分解问题;
    所有分解的子问题处理完成后,整个问题也就处理完了。

    2、在确定分界点的时候,采用了随机数,先用rand函数生成一个随机数,然后将其与序列中最后一个数进行交换,然后利用partition进行划分。

    3、对序列进行递归处理时,需要注意递归出口和递归函数的处理。

    二、代码实现

    partition函数详细分析,参考渐增型算法三:划分序列,方法二

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #define ARRAY_LEN 10
    
    // 在[p, q]范围内,返回一个随机整数
    // rand()返回一个0到RAND_MAX之间的伪随机数
    int randomNumber(int p, int q)
    {
        return p + (int)((double)(q - p) * rand() / (RAND_MAX));
    }
    
    static void printfList(char *info, int *array, int len)
    {
        printf("%s", info);
        for(int i = 0; i < len; i++) {
            printf("%d ", array[i]);
        }
        printf("
    ");
        return;
    }
    
    int intGreater(void *x, void *y)
    {
        return *(int *)x - *(int *)y;
    }
    
    // 元素交换
    void swap(void *x, void *y, int size)
    {
        void *temp = (void*)malloc(size);
        memcpy(temp, x, size);
        memcpy(x, y, size);
        memcpy(y, temp, size);
        free(temp);
    }
    /*
     * description: 将序列array按array[r -1]的值为分界点,分成两部分,原地修改
     * input: 序列指针array,元素大小size,p和r为序列下标,即array[p...r],比较函数cmp
     * output: 分界点在重组序列后的下标
    */
    int partition(void *array, int size, int p, int r, int(*cmp)(void *, void *))
    {
        // leftTail:表示被排序列的左半部分(小于分界值)的尾部下标
        int leftTail = p -1;
        int  postNow;
        // 比较值
        void *key = (void*)malloc(size);
        memcpy(key, array + r * size, size);
        // 遍历序列
        for (postNow = p; postNow < r; postNow++) {
            // 当前值小于比较值时,需要交换,保证[p, leftTail]均小于key,当遍历完全,则满足要求
            if (cmp(array + postNow * size, key) <= 0) {
                // 将leftTail下一个元素肯定是大于key的,所以将其与满足if条件的元素交换
                leftTail++;
                // 交换被将if条件检测出的元素与leftTail+1交换,同时更新了leftTail的值
                swap(array + leftTail * size, array + postNow *size, size);
            }
            // printfList("partitionV2 test: ", (int*)array, ARRAY_LEN);
        }
        free(key);
        // 遍历完成后,[p, leftTail]均为小于key的元素,则将leftTail+1与序列末尾的key交换即可
        swap(array + (leftTail + 1) * size, array + r *size, size);
        return leftTail + 1;
    }
    
    // 将序列a,选取随机数,进行划分,划分策略见 partition
    long randmizedPartition(void *a, int size, long p, long r, int(*comp)(void *, void *))
    {
        // 随机选取一个数,作为划分点
        int randNumber = randomNumber(p, r);
        // 将划分点与数组最后一个数进行交换,因为partition默认以最后一个值作为划分标准
        swap(a + r * size, a + randNumber * size, size); 
        // 以分界点为标准,将a划分成大于分界点和小于分界点的两个部分[p, q]和[q + 1, r],并返回分界点的下标q
        return partition(a, size, p, r, comp);
    }
    
    // 随机选取分界点后,逐步划分,递归处理
    void quickSort(void *a, int size, long p, long r, int(*comp)(void *, void *))
    {
        if (p < r) {
            // 针对序列a的[p, r]部分,进行分组,返回分界值的下标
            long q = randmizedPartition(a, size, p, r, comp);
            // 根据分界值,继续处理子问题
            quickSort(a, size, p, q, comp);
            quickSort(a, size, q + 1, r, comp);
        }
    }
    
    

    三、测试结果

    测试代码:

    int main(void)
    {
        //int array[ARREY_LEN] = {9, 4, 7, 9, 2, 3, 5, 6, 8, 10};
        int array[] = {9, 8, 1, 6, 5, 7, 3, 2, 4, 0};
        int arrayLen = sizeof(array) / sizeof(array[0]);
        int ret;
        printfList("list before quickSort: ", array, arrayLen);
    
        quickSort(array, sizeof(int), 0, arrayLen - 1, intGreater);
        printfList("list after quickSort: ", array, arrayLen);
    
        while (1);
        return 0;
    }
    

    测试结果:

  • 相关阅读:
    去掉滚动条
    一些input用法
    jquery-ui datepicker
    js修改样式
    js时间
    跳转到页面的某个anchor
    事件传递
    flex对象.show()的时候display变成block
    html中传递信息
    button disable and enable
  • 原文地址:https://www.cnblogs.com/HZL2017/p/14382264.html
Copyright © 2020-2023  润新知