合并排序3方式拆分C.

时间:2017-09-22 17:23:17

标签: c mergesort

很抱歉在这里烦人,但我的合并排序代码存在问题。以前我可以编写双向合并排序代码,当我尝试将我的合并排序调整为3向合并排序时,我的编译器给了我:9154 segmentation fault(core dumped)。你能帮我解决这个问题吗?有我的代码:

#include <stdio.h>

void merge(int v[], int p, int q, int r) {
    int i, j, k, b[10];
    for (i = p; i <= q; i++) {
        b[i] = v[i];
    }
    for (j = q + 1; j <= r; j++) {
        b[r + q + 1 - j] = v[j];
    }
    i = p;
    j = r;
    for (k = p; k <= r; k++) {
        if (b[i] <= b[j]) {
            v[k] = b[i];
            i++;
        } else {
            v[k] = b[j];
            j--;
        }
    }
}
void mersort(int v[], int p, int r) { //2-way mergesort that works
    int q;
    if (p < r) {
        q = (p + r) / 2;
        mersort(v, p, q);
        mersort(v, q + 1, r);
        merge(v, p, q, r);
    }
}
void mersort3(int v[], int p, int r) {//not working
    int q, s;
    if (r > p) {
        q = p + (p + r) / 3;
        s = p + 2 * ((p + r) / 3) + 1;
        mersort3(v, p, q);
        mersort3(v, q, s);
        mersort3(v, s, r);
        merge(v, p, q, s);
        merge(v, p, s, r);
    }
}

1 个答案:

答案 0 :(得分:2)

您的主要问题是由于索引计算不当导致的超出范围索引,以及出于同样原因的潜在无限递归。

你过度复杂化了。 C的一个很好的优点是pointer arithmetic,这使得序列分区和遍历这样的事情变得非常简单。作为奖励,您还可以删除一个函数参数,因为它的唯一原因是基本调整,它由指针算法处理。

例如,一个简单的基于VLA的合并算法(显然,不要用大序列调用它)

/* a simple little VLA-based merge. don't invoke with huge arrays */
void merge(int v[], size_t mid, size_t len)
{
    if (len < 2)
        return;

    size_t i=0, j=mid, k=0;
    int tmp[len];

    while (i < mid && j < len)
        tmp[k++] = (v[i] < v[j]) ? v[i++] : v[j++];

    memcpy(tmp+k, v+i, (mid-i) * sizeof *v);
    memcpy(v, tmp, (k + (mid-i)) * sizeof *v);
}

嗯,不是指针算术的一个很好的展示,但那里有一些。它真正闪耀的地方是分区算法。例如,一个简单的合并排序:

void mersort(int v[], size_t len)
{
    if (len < 2)
        return;

    size_t mid = len/2;
    mersort(v, mid);
    mersort(v+mid, len-mid); // see here.
    merge(v, mid, len);
}

将其扩展为三向分区方案:

void mersort3(int v[], size_t len)
{
    if (len < 3)
    {
        mersort(v, len);
        return;
    }

    size_t m1 = len/3;
    size_t m2 = (2 * len)/3;
    mersort3(v, m1);
    mersort3(v+m1, m2-m1);   // see here
    mersort3(v+m2, len-m2);  // and here
    merge(v, m1, m2);
    merge(v, m2, len);
}

使用保证异常分区大小的示例调用如下(序列长度为29个元素)

int main()
{
    srand((unsigned)time(NULL));

    const size_t N = 29;
    size_t i,j;
    int ar[N], n=0;

    // build a sequence from 1..29
    for (i=0; i<N; ++i)
        ar[i] = ++n;

    // shuffle the sequence
    for (i=0; i<3; ++i)
    {
        for (j=0; j<N; ++j)
        {
            n = rand() % N;
            int tmp = ar[n];
            ar[n] = ar[j];
            ar[j] = tmp;
        }
    }

    // show the shuffled sequence
    for (i=0; i<N; ++i)
        printf("%d ", ar[i]);
    fputc('\n', stdout);

    // sort it
    mersort3(ar, N);

    // show it again
    for (i=0; i<N; ++i)
        printf("%d ", ar[i]);
    fputc('\n', stdout);

    return EXIT_SUCCESS;
}

输出(改组后的序列不同)

21 8 11 27 18 9 17 28 20 14 15 1 29 6 19 22 7 2 16 23 5 12 4 3 10 26 13 25 24 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29