【问题标题】:Improper result of Mergesort programMergesort 程序的错误结果
【发布时间】:2020-05-26 10:53:54
【问题描述】:

我正在尝试实现 Mergesort 程序,该程序与 n 元素数组一起工作,如下所示。在这个程序中,我没有创建两个单独的数组 LR,它们在左侧和右侧包含数组 a 的所有数量,而是尝试将其缩减为一个具有相同数量元素的数组数组a

#include <stdio.h>

#define MAX 1000

void merge(int a[], int l, int m, int r) {
    int i, j, k = 0; 
    int b[r + 1];
    for (i = m; i >= 0; i--)
        b[i] = a[i]; 
    for (j = m + 1; j < r; j++) 
        b[r + m - j] = a[j]; 
    while (i <= m && j > m) { 
        if (b[i] <= b[j]) { 
            a[k] = b[i];
            i++; 
        } else { 
            a[k] = b[j];
            j--; 
        } 
        k++; 
    }
    while (i <= m) { 
        a[k] = b[i];
        i++;
        k++; 
    } 
    while (j > m) { 
        a[k] = b[j];
        j--;
        k++; 
    } 
} 

void mergesort(int a[], int l, int r) {
    int i, j, k, mid;
    int b[r + 1];
    if (r > l) {
        mid = (l + r) / 2;
        mergesort(a, l, mid);
        mergesort(a, mid + 1, r);
        merge(a, l, mid, r);
    }
}

int main() {
    int n;
    int i = 0;
    int a[MAX];
    printf("Enter the number of element you want to create: ");
    scanf("%d", &n);
    while (i < n) {
        printf("Enter your number %d: ", i);
        scanf("%d", &a[i]);
        i++;
    }
    mergesort(a, 0, n);
    i = 0;
    while (i < n) {
        printf("%d ", a[i]);
        i++;
    }
    return 0;
}

当我运行这个程序时,它给了我不正确的结果,我不知道我哪里出错了。

【问题讨论】:

  • 我猜测merge() 中的第二个循环正在发送歪斜的东西:for (j = m+1; j &lt; r; j++) b[r+m-j] = a[j]; 可能是写错了地方。 (我认为它会将数组上半部分的元素以相反的顺序复制到b 中——但我愿意被证明是错误的;我只是在心理上进行了分析,而不是使用编译器)。我不知道你为什么不简单地使用for (int i = l; i &lt; r; i++) b[i] = a[i];,然后将正确的元素从b复制回a以合并顺序。
  • 对于右侧部分,我需要将所有元素从 r 复制到 m 到数组 b 但数组 a 仍然从 mr
  • 您是如何尝试调试代码的?我添加了打印代码以在进入和退出merge()mergesort() 时打印数组,并且merge() 函数生成1 的列表,而不是原始数据中提供的值(我所有的值都在范围10..99)。我发现编写的merge() 函数完全违反直觉。它根本不是按照我的思维方式写的。为了我的钱,你应该完全重写它。 (我可以肯定merge() 函数也在读取和/或写入越界。)
  • 还有其他问题,当你第一次进入循环while (i&lt;=m &amp;&amp; j&gt;m)merge()i小于0(-1),m等于r。因此,第一个比较是越界访问数组。第一个for 循环也不应该倒数到0;它应该倒计时到l。正如我已经说过的,merge() 函数存在很多问题。
  • 使用“相关”部分的算法:stackoverflow.com/questions/29274947/… 它有一个正确的算法。特别是,它使用反向索引。

标签: c algorithm loops sorting merge


【解决方案1】:

警告:为了速度/疲劳,我从我在顶级 cmets 中提到的链接中借用了 merge 并对其进行了一些清理。

而且,我将变量重命名为更接近您的。

它匹配我之前写的合并函数。

一个区别是它在最后执行从ba 的回写操作,而不是在开始时执行从ab 的复制操作。

而且,正如我上面提到的,它不会以负方向遍历数组 [我以前没有见过这种方法]。

此外,无论lmr 的值如何,临时数组b 始终从0 开始索引。这稍微简化了一些事情。

为了调试,我稍微更改了main 以使其进行自动最坏情况测试并在最后进行排序检查,以及一些调试printf

#include <stdio.h>
#include <stdlib.h>

#define MAX 1000

void
merge(int a[], int l, int m, int r)
{
    static int b[MAX];
    int i;
    int j;
    int k;

    printf("merge: ENTER l=%d m=%d r=%d\n",l,m,r);

    k = 0;
    i = l;
    j = m + 1;

    for (;  (i <= m) && (j <= r);  ++k) {
        if (a[i] <= a[j])
            b[k] = a[i++];
        else
            b[k] = a[j++];
    }

    for (;  i <= m;  ++i, ++k)
        b[k] = a[i];

    for (;  j <= r;  ++j, ++k)
        b[k] = a[j];

    k = 0;
    for (i = l;  i <= r;  ++i, ++k)
        a[i] = b[k];

    printf("merge: EXIT\n");
}

void
merge_original(int a[], int l, int m, int r)
{
    int i,
     j,
     k = 0;

    printf("merge: ENTER l=%d m=%d r=%d\n",l,m,r);

#if 1
    int b[r + 1];
    for (i = m; i >= 0; i--)
        b[i] = a[i];
#endif

    for (j = m + 1; j < r; j++)
        b[r + m - j] = a[j];

    while (i <= m && j > m) {
        if (b[i] <= b[j]) {
            a[k] = b[i];
            i++;
        }
        else {
            a[k] = b[j];
            j--;
        }
        k++;
    }

    while (i <= m) {
        a[k] = b[i];
        i++;
        k++;
    }

    while (j > m) {
        a[k] = b[j];
        j--;
        k++;
    }

    printf("merge: EXIT\n");
}

void
mergesort(int a[], int l, int r)
{
    int i,
     j,
     k,
     mid;
    int b[r + 1];

    if (r > l) {
        mid = (l + r) / 2;
        mergesort(a, l, mid);
        mergesort(a, mid + 1, r);
        merge(a, l, mid, r);
    }
}

int
main(void)
{
    int n;
    int i;
    int a[MAX];

    printf("Enter the number of element you want to create: ");
    scanf("%d", &n);

    for (i = 0;  i < n;   ++i) {
#if 0
        printf("Enter your number %d: ", i);
        scanf("%d", &a[i]);
#else
        a[i] = n - i;
#endif
        printf("SET %d: %d\n", i, a[i]);
    }

    mergesort(a, 0, n);

    int oval = -1;
    int cval;
    for (i = 0;  i < n;   ++i, oval = cval) {
        cval = a[i];

        printf("CMP %d: %d\n", i, cval);

        if (cval < oval) {
            printf("fault\n");
            exit(1);
        }
    }

    return 0;
}

更新:

我有个问题,在merge函数中,这里第三个for循环的作用是什么?

请注意,[在两者中您的代码和更新的代码]b 是临时数组。

一共有四个循环:

(1) “合并”循环 [取任一子数组中的最低者,而 两个 子数组仍有剩余元素]:

for (;  (i <= m) && (j <= r);  ++k)

(2) 第二个循环将低/左子数组中的所有元素复制到目标如果它的剩余元素多于高/右子数组:

for (;  i <= m;  ++i, ++k)

(3) 第三个循环将高/右子数组中的所有元素复制到目标如果它的剩余元素多于低/左子数组:

for (;  j <= r;  ++j, ++k)

(4)第四个循环将[temp]数组b复制回原数组a

for (i = l;  i <= r;  ++i, ++k)

请注意,只有 一个 循环 (2) 或循环 (3) 会实际复制数据。这是因为循环 (1) 保证了 i &lt;= m j &lt;= r 在完成后将是false

在您的原始代码中,您的前两个循环 [仅] 从 a 中的低/高子数组复制到 b。然后,您从b 合并回a [在三个循环中]。

在修改后的代码中,我们从a 合并到b [在前三个循环中]。然后,我们从 [temp] b 数组复制回 a

所以,我们必须使用临时数组。但是,我们可以一开始就复制进去。 或者,我们可以在最后拷贝出来。两种方法都行。

请记住,我提到过 b 数组是从零开始索引的。这样可以减少一个循环(四个对五个)

【讨论】:

  • 我有个问题,在merge函数中,这里的第三个for循环的作用是什么?
【解决方案2】:

这段代码展示了我是如何检测算法的。最初,它是通过对printf()dump_array() 的无条件调用来完成的——当它工作时,它们被转换为宏,以便可以使用-DDEBUG 编译代码以获得详细的跟踪或不只获取结果。

我没有删除提示代码,但我自己的程序不会这样做。我可能还会让程序计算输入了多少值(使用动态内存分配为数据腾出空间)并简单地读取数据,直到它达到 EOF。为了测试,我使用了保存在文件中的三组随机生成的数字,以便可以重复测试。

我没有尝试修复原始的merge() 代码——我只是重写了它。我的问题概述了原始代码的一些问题。我相当确定问题中的代码没有充分注意哪些数字范围是有效的。此代码注释mergesort()merge() 的范围包括结束索引(因此main() 调用mergesort(a, 0, n - 1) 而不是问题中的mergesort(a, 0, n),例如)。

此合并代码首先将数据复制到b 数组中,然后从b 合并回a(或多或少类似于问题中的代码)——合并到b 中是可行的然后再从b复制到a

#include <assert.h>
#include <stdio.h>
#define MAX 1000

#ifdef DEBUG
#define TRACE(...)          printf(__VA_ARGS__)
#define DUMP(t, a, l, r)    dump_array(t, a, l, r)
#else
#define TRACE(...)          ((void)0)
#define DUMP(t, a, l, r)    ((void)0)
#endif

/* Dump elements in range l <= i <= r */
static void dump_array(const char *tag, int a[], int l, int r)
{
    assert(tag != 0 && a != 0 && l >= 0 && l <= r);
    printf("%s[%d..%d]:", tag, l, r);
    for (int i = l; i <= r; i++)
        printf(" %d", a[i]);
    putchar('\n');
}

/* Merge elements in ranges l <= i <= m and m+1 <= j <= r */
static void merge(int a[], int l, int m, int r)
{
    assert(a != 0 && l >= 0 && l <= m && m <= r);
    TRACE("-->> %s(): %d..%d..%d\n", __func__, l, m, r);
    int b[r + 1];
    for (int i = l; i <= r; i++)
        b[i] = a[i];
    DUMP("b", b, l, r);
    int k = l;
    int i = l;
    int j = m + 1;
    while (i <= m && j <= r)
    {
        if (b[i] <= b[j])
            a[k++] = b[i++];
        else
            a[k++] = b[j++];
    }
    while (i <= m)
        a[k++] = b[i++];
    while (j <= r)
        a[k++] = b[j++];
    DUMP("a", a, l, r);
    TRACE("<<-- %s(): %d..%d..%d\n", __func__, l, m, r);
}

/* Sort elements in range l <= i <= r */
static void mergesort(int a[], int l, int r)
{
    assert(a != 0 && l >= 0 && l <= r);
    if (r > l)
    {
        TRACE("-->> %s(): %d..%d\n", __func__, l, r);
        DUMP("a", a, l, r);
        int mid = (l + r) / 2;
        mergesort(a, l, mid);
        DUMP("a", a, l, mid);
        mergesort(a, mid + 1, r);
        DUMP("a", a, mid+1, r);
        merge(a, l, mid, r);
        DUMP("a", a, l, r);
        TRACE("<<-- %s(): %d..%d\n", __func__, l, r);
    }
}

int main(void)
{
    int n;
    int a[MAX];
    printf("Enter the number of elements you want to create: ");
    if (scanf("%d", &n) != 1 || n < 1 || n > MAX)
    {
        fprintf(stderr, "read error\n");
        return 1;
    }
    for (int i = 0; i < n; i++)
    {
        printf("Enter your number %d: ", i);
        if (scanf("%d", &a[i]) != 1)
        {
            fprintf(stderr, "read error\n");
            return 1;
        }
    }
    putchar('\n');

    dump_array("Unsorted", a, 0, n - 1);
    mergesort(a, 0, n - 1);
    dump_array("Sorted", a, 0, n - 1);

    return 0;
}

示例运行(-DDEBUG;包含 5 个元素的数据集 — 当然,打印出来是为了便于检查):

Enter the number of elements you want to create: Enter your number 0: Enter your number 1: Enter your number 2: Enter your number 3: Enter your number 4: 
Unsorted[0..4]: 17 74 63 62 26
-->> mergesort(): 0..4
a[0..4]: 17 74 63 62 26
-->> mergesort(): 0..2
a[0..2]: 17 74 63
-->> mergesort(): 0..1
a[0..1]: 17 74
a[0..0]: 17
a[1..1]: 74
-->> merge(): 0..0..1
b[0..1]: 17 74
a[0..1]: 17 74
<<-- merge(): 0..0..1
a[0..1]: 17 74
<<-- mergesort(): 0..1
a[0..1]: 17 74
a[2..2]: 63
-->> merge(): 0..1..2
b[0..2]: 17 74 63
a[0..2]: 17 63 74
<<-- merge(): 0..1..2
a[0..2]: 17 63 74
<<-- mergesort(): 0..2
a[0..2]: 17 63 74
-->> mergesort(): 3..4
a[3..4]: 62 26
a[3..3]: 62
a[4..4]: 26
-->> merge(): 3..3..4
b[3..4]: 62 26
a[3..4]: 26 62
<<-- merge(): 3..3..4
a[3..4]: 26 62
<<-- mergesort(): 3..4
a[3..4]: 26 62
-->> merge(): 0..2..4
b[0..4]: 17 63 74 26 62
a[0..4]: 17 26 62 63 74
<<-- merge(): 0..2..4
a[0..4]: 17 26 62 63 74
<<-- mergesort(): 0..4
Sorted[0..4]: 17 26 62 63 74

示例运行(-UDEBUG 有 12 个元素 - 5 元素集使用该集中的前 5 个数字):

Enter the number of elements you want to create: Enter your number 0: Enter your number 1: Enter your number 2: Enter your number 3: Enter your number 4: Enter your number 5: Enter your number 6: Enter your number 7: Enter your number 8: Enter your number 9: Enter your number 10: Enter your number 11: 
Unsorted[0..11]: 17 74 63 62 26 16 15 86 80 17 35 69
Sorted[0..11]: 15 16 17 17 26 35 62 63 69 74 80 86

示例运行(-UDEBUG 包含 30 个元素 - 完全是一组单独的随机数):

Enter the number of elements you want to create: Enter your number 0: Enter your number 1: Enter your number 2: Enter your number 3: Enter your number 4: Enter your number 5: Enter your number 6: Enter your number 7: Enter your number 8: Enter your number 9: Enter your number 10: Enter your number 11: Enter your number 12: Enter your number 13: Enter your number 14: Enter your number 15: Enter your number 16: Enter your number 17: Enter your number 18: Enter your number 19: Enter your number 20: Enter your number 21: Enter your number 22: Enter your number 23: Enter your number 24: Enter your number 25: Enter your number 26: Enter your number 27: Enter your number 28: Enter your number 29: 
Unsorted[0..29]: 80 76 86 10 46 99 12 55 76 34 86 23 76 83 13 21 93 50 19 53 86 92 81 35 90 53 65 67 79 20
Sorted[0..29]: 10 12 13 19 20 21 23 34 35 46 50 53 53 55 65 67 76 76 76 79 80 81 83 86 86 86 90 92 93 99

我检查了 30 个元素的集合——获取未排序的数字列表,用sort -n 对它们进行排序,然后将该列表与排序后的输出进行比较。结果是相同的,这意味着排序的“排序顺序”和“守恒”属性是有效的。我观察了 5 和 12 组数据值的结果。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多