在C中进行合并排序时保持获取垃圾值

时间:2013-09-05 05:06:16

标签: c sorting mergesort

我正在研究通用的合并排序算法。现在的问题是,当我打印所谓的“已排序”数组的内容时,我一直在获取垃圾值。

合并排序算法:

void merge(void *a, int n, int size, int (*fcmp)(const void *, const void *)){
    int i, j, k, mid=n/2;
    void * temp = (void *)malloc(n*size);

    for(i=0, j=mid, k=0; k<n; k++){
        if((i<mid)&&(j>=  n)){
            memcpy(temp+(k*size), a+i*size, size);
            i++;
        }
        else if((i<mid)&&(fcmp(a + i*size, a+j*size) <= 0)){
            memcpy(temp+(k*size), a+j*size, size);
            j++;
        }
    }

    for(i=0, j=0; j<n; i++, j++)
        memcpy(a+(j*size),temp+(i*size),size);
    free(temp);
}

void genmsort(void *a, int n, int size, int (*fcmp)(const void *, const void *)){
    if(n>1){
        genmsort(a, n/2, size, (int(*)(const void *, const void *)) compnode);
        genmsort(a+(n/2)*size, n-n/2, size, (int(*)(const void *, const void *)) compnode);
        merge(a, n, size, (int(*)(const void *, const void *)) compnode);
    }
}

compnode函数:

int compnode(node *a, node *b){
    return (strcmp(a->name, b->name));
}

初始化函数:

void init_node(node a[], int n){
int i;
    for(i=0; i<n; i++){
        a[i].stdno=i+1;
        sprintf(a[i].name, "%li", a[i].stdno);
    }
    srand(8);
    for(i=0; i<n; i++)
        genswap(a+i, a+(rand()%n), sizeof(node));
}

主要功能:

int main(){
    int n=10;
    clock_t t1, t2;

    node *b;
    b=(node *)malloc(n*sizeof(node));
    init_node(b, n);

    t1=clock();
    genmsort(b, n, sizeof(node), (int(*)(const void *, const void *)) compnode);

    t2=clock();
    free(b);
}

这里有什么问题?对于冗长的代码我很抱歉,但我希望你能理解它。我非常感谢你的帮助,因为我现在已经坚持使用这段代码。

2 个答案:

答案 0 :(得分:2)

辅助问题

从评论转录到问题。

为了怜悯,请谨慎写下compnode(),这样你就不必经历可怕的演员了!写它来取两个const void *参数并在代码中转换它们(它将是一个无操作):

int compnode(const void *v1, const void *v2)
{
    const node *a = v1;
    const node *b = v2;
    return strcmp(a->name, b->name);
}

另外,请勿使用GCC的扩展程序。如果您对编写可移植代码有任何自负,这是一个坏习惯。根据C标准编写参数为a+(n/2)*size的{​​{1}}是未定义的行为。在添加之前,您必须转换为void *a(或char *以外的其他类型)。

void *中,您应该将genmnode()传递给递归函数和fcmp函数,而不是直接传递merge()

Gannicus问道:

  

您的意思是通过compnode()代替fcmp

WhozCraig解释说:

  

[It]表示您将自定义比较器函数作为compnode参数传递给“generic”排序函数。在该函数中,您盲目地将fcmp传递给递归调用。您应该将compnode传递给那些递归调用,否则您的“通用”意识形态就会消失。

主要问题

主要问题在于您的fcmp功能。它的界面是最不寻常的。通常,您传递两个要合并的数组以及每个数组的大小。你选择通过一个阵列并做一些花哨的步法。 main for循环中的代码将一切都搞砸了。

merge()

尾随循环应该是单个void merge(void *a, int n, int size, int (*fcmp)(const void *, const void *)){ int i, j, k, mid=n/2; void * temp = (void *)malloc(n*size); for(i=0, j=mid, k=0; k<n; k++){ if((i<mid)&&(j>= n)){ memcpy(temp+(k*size), a+i*size, size); i++; } else if((i<mid)&&(fcmp(a + i*size, a+j*size) <= 0)){ memcpy(temp+(k*size), a+j*size, size); j++; } } for(i=0, j=0; j<n; i++, j++) memcpy(a+(j*size),temp+(i*size),size); free(temp); } 操作,但有什么用呢。

您有一个数组memcpy(),其中包含给定大小的a个元素。它必须被视为两个子阵列,一个是元素[0..mid],一个是LHS,另一个是元素[mid..n],即RHS。范围包括下限和排除上限。

循环内的第一个条件是'如果LHS中有一个元素而RHS中没有剩余,则将LHS元素复制到输出'。第二个条件是'如果LHS中有一个元素(并且通过消除,RHS中也有一个元素),并且LHS比RHS小,则将RHS元素复制到输出'。 / p>

编写合并过程有不同且最终等效的方法,但是 通常最容易理解的是:

n

实现的循环并不接近实现该逻辑或其等价物。


工作代码

这是我的代码诊断版本。 while (item left in LHS and item left in RHS) { if (item in LHS is smaller than item in RHS) copy LHS to result else copy RHS to result } while (item left in LHS) copy item to result while (item left in RHS) copy item to result 顶部的memset()无关紧要;你应该复制到merge()并写下所有的X.在实践中,你不是。

temp

答案 1 :(得分:2)

此代码中的事物的纬度很丰富。有些是show-stoppers,但最终它是你的合并功能。典型的合并算法会在每次迭代时将 一个 项目移动到目标缓冲区中,直到一个列表或另一个列表耗尽为止。一旦发生这种情况,剩余列表中的剩余项目将被批量复制到位,算法将终止。

你有一个根本性的缺陷,我们现在将介绍。你的主循环运行k一直到n,至少是正确的。但是,请查看if-else-if条件中的第一个表达式:

    if((i<mid)&&(j>=  n))
    {
        memcpy(temp+(k*size), a+i*size, size);
        i++;
    }
    else if((i<mid)&&(fcmp(a + i*size, a+j*size) <= 0))
    {
        memcpy(temp+(k*size), a+j*size, size);
        j++;
    }

他们这两个都有i<mid,所以这可以简化为:

    if (i<mid)
    {
        if (j>=n)
        {
            memcpy(temp+(k*size), a+i*size, size);
            i++;
        }
        else if (fcmp(a + i*size, a+j*size) <= 0))
        {
            memcpy(temp+(k*size), a+j*size, size);
            j++;
        }
    }

这意味着,如果您的i方面 j方面已经耗尽,那么您只需从该点开始 ,只需递增k,直至达到n。拆分列表的j侧的其余部分完全被忽略。然后,在函数结束时,将未初始化的数据复制到原始数组的顶部。

要考虑的一些事情。首先,键入你的比较器功能要求并坚持下去。比较器有责任遵守回调请求者的要求;不是相反。

typedef int (*fn_cmp)(const void*, const void*);

并通过实现对该标准的回调来正确使用

// compare two nodes.
int compare_node(const void* lhs, const void* rhs)
{
    const node* lhn = lhs;
    const node* rhn = rhs;
    return (strcmp(lhn->name, rhn->name));
}

这也使您的通用mergesort 更多更清洁:

// generic mergesort algorithm
void genmsort(void *src, unsigned int len, unsigned int size, fn_cmp fcmp)
{
    if (len < 2)
        return;

    unsigned int mid = len/2;
    genmsort(src, mid, size, fcmp);
    genmsort((unsigned char*)src+(mid*size), len - mid, size, fcmp);
    merge(src, mid, len-mid, size, fcmp);
}

除了可读性之外,以下merge与您之间的最大区别是添加了第二个长度参数(事实上这个工作被视为奖励)。您的代码从最初传入的单个长度中推断出此值;在计算递归分区大小时,您在代码中完全独立的位置执行的操作由于包含一致性和可用性的多种原因,这些相同的大小也需要在此处传递。

请考虑以下 。如果可以更好地注释这个算法,或者使它更清晰,我就不知道如何:

// merges two lists back to back in a single sequence.
void merge(void *src,
           unsigned int alen, // note parition size.
           unsigned int blen, // and again here.
           unsigned int size,
           fn_cmp fcmp)
{
    void *bsrc = (unsigned char*)src + alen * size;
    void *dst = malloc((alen + blen)*size);
    unsigned int a = 0, b = 0, k = 0;

    for (k=0; k<(alen+blen); ++k)
    {
        // still got a's ?
        if (a < alen)
        {
            // still got b's ?
            if (b < blen)
            {
                // get "lesser" of the two.
                if (fcmp((const unsigned char*)src + a*size,
                         (const unsigned char*)bsrc + b*size) <= 0)
                {
                    // a is less. move it in.
                    memcpy((unsigned char *)dst + k*size,
                           (const unsigned char*)src + a++*size, size);
                }
                else
                {   // b is less. move it in.
                    memcpy((unsigned char *)dst + k*size,
                           (const unsigned char*)bsrc + b++*size, size);
                }
            }
            else
            {   // no more b's. move the rest of the a's
                // into the target and leave.
                memcpy((unsigned char *)dst + k*size,
                       (const unsigned char*)src + a*size, (alen - a)*size);
                k += (alen-a);
            }
        }
        else
        {   // else no a's. move the rest of the b's into
            //  the target and leave.
            memcpy((unsigned char *)dst + k*size,
                   (const unsigned char*)bsrc + b*size, (blen - b)*size);
            k += (blen-b);
        }
    }

    // copy final output.
    memcpy(src, dst, (alen+blen)*size);
    free(dst);
}

最后,这些代码需要任何编译器扩展,例如您在代码中大量利用的违反标准的增量void*。我强烈建议您不要使用此类扩展程序。


以下是用于验证上述算法及其界面的完整测试程序。仔细阅读

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include <time.h>

// simple node definition.
typedef struct node
{
    char name[32];
    int id;
} node;

// compare two nodes.
int compare_node_names(const void* lhs, const void* rhs)
{
    const node* lhn = lhs;
    const node* rhn = rhs;
    return (strcmp(lhn->name, rhn->name));
}

// compare two nodes.
int compare_node_ids(const void* lhs, const void* rhs)
{
    const node* lhn = lhs;
    const node* rhn = rhs;
    return (lhn->id  - rhn->id);
}

// comparator requirements.
typedef int (*fn_cmp)(const void*, const void*);

// merges two lists back to back in a single sequence.
void merge(void *src,
           unsigned int alen, // note parition size.
           unsigned int blen, // and again here.
           unsigned int size,
           fn_cmp fcmp)
{
    void *bsrc = (unsigned char*)src + alen * size;
    void *dst = malloc((alen + blen)*size);
    unsigned int a = 0, b = 0, k = 0;

    for (k=0; k<(alen+blen); ++k)
    {
        // still got a's ?
        if (a < alen)
        {
            // still got b's ?
            if (b < blen)
            {
                // get "lesser" of the two.
                if (fcmp((const unsigned char*)src + a*size,
                         (const unsigned char*)bsrc + b*size) <= 0)
                {
                    // a is less. move it in.
                    memcpy((unsigned char *)dst + k*size,
                           (const unsigned char*)src + a++*size, size);
                }
                else
                {   // b is less. move it in.
                    memcpy((unsigned char *)dst + k*size,
                           (const unsigned char*)bsrc + b++*size, size);
                }
            }
            else
            {   // no more b's. move the rest of the a's
                // into the target and leave.
                memcpy((unsigned char *)dst + k*size,
                       (const unsigned char*)src + a*size, (alen - a)*size);
                k += (alen-a);
            }
        }
        else
        {   // else no a's. move the rest of the b's into
            //  the target and leave.
            memcpy((unsigned char *)dst + k*size,
                   (const unsigned char*)bsrc + b*size, (blen - b)*size);
            k += (blen-b);
        }
    }

    // copy final output.
    memcpy(src, dst, (alen+blen)*size);
    free(dst);
}

// generic mergesort algorithm
void genmsort(void *src, unsigned int len, unsigned int size, fn_cmp fcmp)
{
    if (len < 2)
        return;

    unsigned int mid = len/2;
    genmsort(src, mid, size, fcmp);
    genmsort((unsigned char*)src+(mid*size), len - mid, size, fcmp);
    merge(src, mid, len-mid, size, fcmp);
}

int main()
{
    static const unsigned int N = 50;
    node *data = malloc(N * sizeof(*data));
    int i=0;

    srand((unsigned)time(NULL));
    for (i=0;i<N;++i)
    {
        data[i].id = i+1;
        sprintf(data[i].name, "String%.3d", 1 + rand() % 999);
    }

    // sort on names.
    genmsort(data, N, sizeof(data[0]), compare_node_names);
    for (i=0;i<N;++i)
        printf("%s : %u\n", data[i].name, data[i].id);
    printf("\n");

    // use a different comparator, this time by id.
    genmsort(data, N, sizeof(data[0]), compare_node_ids);
    for (i=0;i<N;++i)
        printf("%s : %u\n", data[i].name, data[i].id);
    printf("\n");

    free(data);

    return 0;
}

<强>输出

String053 : 49
String097 : 38
String104 : 46
String122 : 41
String129 : 8
String139 : 3
String168 : 30
String184 : 22
String222 : 16
String230 : 28
String249 : 4
String265 : 34
String285 : 44
String295 : 20
String298 : 47
String300 : 19
String321 : 2
String375 : 37
String396 : 50
String408 : 13
String430 : 31
String466 : 35
String483 : 24
String484 : 27
String491 : 25
String494 : 39
String507 : 10
String513 : 7
String514 : 11
String539 : 5
String556 : 29
String570 : 43
String583 : 33
String584 : 42
String620 : 15
String632 : 12
String671 : 21
String705 : 23
String710 : 14
String714 : 45
String724 : 18
String733 : 9
String755 : 48
String805 : 36
String814 : 6
String847 : 32
String876 : 40
String893 : 26
String906 : 17
String972 : 1

String972 : 1
String321 : 2
String139 : 3
String249 : 4
String539 : 5
String814 : 6
String513 : 7
String129 : 8
String733 : 9
String507 : 10
String514 : 11
String632 : 12
String408 : 13
String710 : 14
String620 : 15
String222 : 16
String906 : 17
String724 : 18
String300 : 19
String295 : 20
String671 : 21
String184 : 22
String705 : 23
String483 : 24
String491 : 25
String893 : 26
String484 : 27
String230 : 28
String556 : 29
String168 : 30
String430 : 31
String847 : 32
String583 : 33
String265 : 34
String466 : 35
String805 : 36
String375 : 37
String097 : 38
String494 : 39
String876 : 40
String122 : 41
String584 : 42
String570 : 43
String285 : 44
String714 : 45
String104 : 46
String298 : 47
String755 : 48
String053 : 49
String396 : 50