当前位置: 首页 > 知识库问答 >
问题:

C:自顶向下合并排序-为什么是无限递归?

云瑞
2023-03-14

我正在尝试实现合并排序,其中原始数组和辅助数组为每个递归交替。它基于Java代码。描述如下(链接):

改进。通过对实现进行一些经过仔细考虑的修改,我们可以大幅缩短mergesort的运行时间。

[...]

  • 消除到辅助阵列的副本。可以消除复制到用于合并的辅助阵列所需的时间(但不是空间)。为此,我们使用了两个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进行分析后,我的代码似乎进行了无限递归(消息是“不能增长堆栈”)。

为什么我的实现做无限递归?

共有1个答案

何浩荡
2023-03-14

也许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);
 类似资料:
  • 我正在尝试理解递归排序函数,它是mergesort算法的一部分。下面是我的代码,我几乎可以肯定它是正确的(通过在线课程)。 我理解合并的作用——它将每个子数组分解成两个较小的子数组,重复这个过程,直到子数组的长度为1(根据定义排序),然后合并。然而,这个排序函数用来完成这个任务的实际方法对我来说很难理解。也许是因为我不习惯递归函数,但是我想知道是否有人可以在第一次合并发生时阐明操作的顺序和参数是什

  • 我正在维基百科上阅读关于外部排序的文章,我需要理解为什么两阶段合并比一阶段合并更有效。 Wiki:但是,单次合并有一个限制。随着区块数量的增加,我们将内存分成更多的缓冲区,因此每个缓冲区都较小,因此我们必须进行许多较小的读取,而不是较少的较大读取。 因此,对于100 MB内存中的50 GB的排序,使用单个合并过程是没有效率的:磁盘需要用500个数据块中的每个数据块(我们一次从每个数据块读取100M

  • 双向合并排序与递归合并排序有何不同? 假设在合并排序中有5个数字需要排序8,9,1,6,4,我们按如下步骤1进行划分:{8,9,1}{6,4} 步骤2:{8,9}{1}{6}{4} 步骤3:{8}{9}{1}{6}{4} 现在合并 步骤4:{8,9}{1}{4,6} 步骤5:{1,8,9}{4,6} 第六步:{1,4,6,8,9} 但在双向合并排序中,我们将数组分为两个元素(但根据维基百科,在合并

  • 我读过这些话: 为了使动态规划适用,一个问题必须具有两个关键属性:最优子结构和重叠子问题。如果一个问题可以通过组合非重叠子问题的最优解来解决,那么这个策略就叫做“分而治之”。这也是为什么mergesort和quicksort没有被归类为动态规划问题的原因。 我有三个问题: 为什么合并排序和快速排序不是动态编程? 我认为合并排序也可以将小问题和小问题分开,然后做同样的事情等等。 Dijkstra算法

  • 问题内容: 我们知道快速排序是最快的排序算法。 JDK6 使用合并排序算法而不是快速排序。但是Arrays.sort使用快速排序算法。 Collections.sort使用合并排序而不是快速排序的原因是什么? 问题答案: 极有可能从乔希布洛赫§: 我确实写了这些方法,所以我想我有资格回答。确实没有最佳的排序算法。与mergesort相比,QuickSort有两个主要缺陷: 它不稳定(如parsif

  • 但实际发生的情况是,rndNumber包含一个0到100之间的随机整数。为什么会这样呢? 我明白上限是排他性的,但那为什么下限是包容性的呢?为什么这不一致呢?