渐增型算法三:划分序列

一、问题描述

提供序列A[p...r],要求以A[r]为分界值,将原序列分两部分;
其中A[p...q-1]中元素值小于分界值,A[q+1...r]中的元素值大于分界值;
返回新序列中的分界值下标q

二、方法一:逐个比较元素,序列整体移动

说明:逐个比较序列值,若是其值大于分界值,则将其移到序列尾部,序列其他值前移;可能需要频繁移动数组,效率较低

int partitionV1(void *array, int size, int p, int r, int(*cmp)(void *, void *))
{
    // keyPost 标记分界值的位置
    int keyPost = r;
    void *key = (void*)malloc(size);
    memcpy(key, array + r * size, size);
    void *arrayTemp = (void*)malloc(size);
    // 遍历序列
    for (int i = p; i < keyPost; i++) {
        // 遍历值大于分界值,需要将该遍历值移动到序列尾部
        if (cmp(array + i * size, key) > 0) {
            // 先保存当前遍历值,因为后续移动会覆盖该值
            memcpy(arrayTemp, array + i * size, size);
            // 将array[i + 1...r]均向前移动一位
            for (int j = i; j < r; j++) {
                memcpy(array + j * size, array + (j + 1) * size, size);
            }
            // 将当前遍历移到序列末尾
            memcpy(array + r * size, arrayTemp, size);
            // 分界值会同步向前移动一位,分界值同步前移一位
            keyPost--;
            // 由于序列移动,当前i指向的值已经更新,所以需要将当下的i值再次进入for循环判断,i--与for中i++抵消
            i--;
        }
        printfList("partation test: ", (int*)array, ARRAY_LEN);
    }
    free(arrayTemp);
    free(key);
    return keyPost;
}

三、方法二:元素交换

// 元素交换
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 partitionV2(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;
}

四、测试

测试代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ARRAY_LEN 10

static void printfList(char *info, int *array, int len)
{
    printf("%s", info);
    for(int i = 0; i < len; i++) {
        printf("%d ", array[i]);
    }
    printf("\n");
    return;
}

int intGreater(void *x, void *y)
{
    return *(int *)x - *(int *)y;
}

int main(void)
{
    int array[ARRAY_LEN] = {3, 7, 2, 8, 9, 1, 0, 6, 4, 5};
    printfList("array before partation: ", array, ARRAY_LEN);

    int pos = partitionV1(array, sizeof(int), 0, ARRAY_LEN - 1, intGreater);
    // int pos = partitionV2(array, sizeof(int), 0, ARRAY_LEN - 1, intGreater);
    printfList("array after partation: ", array, ARRAY_LEN);
    printf("pos = %d\n", pos);

    while (1);
    return 0;
}

方法一测试结果:

方法二测试结果:

posted @ 2021-01-23 23:43  Pangolin2  阅读(177)  评论(0编辑  收藏  举报