一、算法思想
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;
}
测试结果: