分治算法三:快速排序

一、算法思想

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;
}

测试结果:

原文地址:https://www.cnblogs.com/HZL2017/p/14382264.html