【问题标题】:C: Top-Down Merge Sort - why infinite recursion?C:自顶向下合并排序 - 为什么无限递归?
【发布时间】:2017-02-02 05:10:07
【问题描述】:

我正在尝试实现合并排序,其中原始数组和辅助数组在每次递归时交替进行。 It's based on a this Java code。描述如下(Link):

改进。我们可以通过对实现进行一些仔细考虑的修改来大幅缩短合并排序的运行时间。

[...]

  • 消除对辅助数组的复制。可以消除复制到用于合并的辅助数组所花费的时间(但不是空间)。为此,我们使用了 sort 方法的两次调用,一次从给定数组中获取输入,并将排序后的输出放入辅助数组;另一个从辅助数组中获取输入,并将排序后的输出放入给定数组中。使用这种方法,在一些令人费解的递归技巧中,我们可以安排递归调用,以便计算在每个级别切换输入数组和辅助数组的角色。

以下 C 代码是我尝试交替两个数组的角色:

#include <stdlib.h>
#include <string.h>

#include "mergesort.h"

#define THRESHOLD 20

static size_t size_m = 0;
static size_t elements = 0;
static size_t mod = 0;
static char *a = NULL;
static char *b = NULL;
static char *i = NULL;
static char *j = NULL;
static char *k = NULL;
static char *start = NULL;
static char *middle = NULL;
static char *end = NULL;
static char *e = NULL;
static int (*cmp_m)(const void *, const void *) = NULL;

void sort(char *a, char *b, size_t lmod, size_t rmod) {
    elements = rmod-lmod+1;

    //========== INSERTION SORT ==========
    if(elements <= THRESHOLD) {
        start = b+size_m*lmod;
        end = b+size_m*rmod;

        for(i = start; i <= end; i += size_m) {

            memcpy(e, i, size_m);
            for(j = i-size_m; j >= start && (*cmp_m)((void *)e, (void *)j) < 0; j -= size_m) {
                memcpy(j+size_m, j, size_m);
            }
            memcpy(j+size_m, e, size_m);
        }

        return;
    }

    //========== SPLIT OPERATION ==========//
    size_t mmod = (rmod-lmod)/2;

    sort(b, a, lmod, mmod);
    sort(b, a, mmod+1, rmod);

    //========== CHECK IF CURRENT SUBARRAY IS ALREADY SORTED ==========//
    if((*cmp_m)((void *)(a+size_m*mmod), (void *)(a+size_m*(mmod+1))) <= 0) {
        memcpy(b+lmod, a+lmod, size_m*elements);
        return;
    }

    //========== MERGE OPERATION ==========//
    start = a+size_m*lmod;
    middle = a+size_m*mmod;
    end = a+size_m*rmod;

    i = start;
    j = middle+size_m;

    for(k = start; k <= end; k += size_m) {
        mod = k-a;

        if(i <= middle && (j > end || (*cmp_m)((void *)i, (void *)j) <= 0)) {
            memcpy(b+mod, i, size_m);
            i += size_m;
        } else {
            memcpy(b+mod, j, size_m);
            j += size_m;
        }
    }
}

void mergesort(void *array, size_t num, size_t size, int (*cmp)(const void *a, const void *b)) {
    size_m = size;
    threshold = THRESHOLD;
    a = (char *)array;
    b = (char *)malloc(num*size_m);
    e = (char *)malloc(size_m);
    cmp_m = cmp;

    memcpy(b, a, size_m*num);
    sort(b, a, 0, num-1);

    free(b);
    free(e);
}

使用 valgrind 进行分析后,我的代码似乎进行了无限递归(消息是“无法增长堆栈”)。

为什么我的实现会进行无限递归?

【问题讨论】:

  • 通过调试器运行它,它会吐出执行的最后一行。然后试着找出原因,如果你不能给我们一个清晰简洁的问题描述。
  • OT:在 C 中不要转换 malloc() 和朋友。避免使用全局变量。
  • valgrind 中的堆栈溢出不一定意味着您的代码中存在错误。如果您有一个堆栈重的应用程序,很可能是 valgrind 导致了溢出 - 原因是,valgrind 在堆栈变量之间引入了保护区,因此可能需要更多堆栈空间 - 从 ulimit -s unlimited 开始,然后重试您的 valgrind 运行。
  • 1) size_t mmod = (rmod-lmod)/2; 错误。
  • 应该是size_t mmod = lmod+(rmod-lmod)/2;

标签: c arrays sorting recursion mergesort


【解决方案1】:

也许,valgrind 无法通过递归判断 元素 减少与否。
试试下面的代码。

static void sort(char *a, char *b, size_t n) {
        :
        :
    //========== SPLIT OPERATION ==========//

    size_t  m = n/2;
    sort(b, a, m);
    sort(b + m * size_m, a + m * size_m, n - m);

【讨论】:

    猜你喜欢
    • 2016-04-14
    • 1970-01-01
    • 2014-01-21
    • 1970-01-01
    • 1970-01-01
    • 2020-01-06
    • 2021-11-06
    • 2019-01-22
    相关资源
    最近更新 更多