归并排序可以节省内存

我正在学习算法课程,最近的作业让我很头疼。基本上,这个任务是要我实现一种版本的归并排序,它不需要像 CLRS 中的实现一样分配太多的临时内存。我应该通过一开始只创建一个临时数组,并在拆分和合并时将所有临时内容放入其中来实现这一点。

我还应该注意到,这个班级的语言是 Lua,这很重要,因为唯一可用的数据结构是表格。它们像 Java 映射一样以键值对形式出现,但它们像数组一样,如果只插入一个东西,它将被视为一个值,它的键将是在具有真实数组的语言中的索引。至少这是我理解的方式,因为我也是新手。此外,任何东西,原语、字符串、对象等都可以作为键——甚至是同一个表中的不同类型。

无论如何,有两件事让我困惑:

首先,它是如何完成的?你只是在每次拆分和合并的递归中用临时数组覆盖吗?

第二,我真的对作业说明感到困惑(我正在免费地旁听课程,所以无法询问任何工作人员)。这里是说明:

1.编写一个顶层过程merge_sort,它以要排序的数组作为参数。它应该声明一个临时数组,然后调用merge_sort_1merge_sort_1是一个具有四个参数的过程:要排序的数组、用作临时空间的数组,以及此调用中merge_sort_1应该工作的开始和结束索引。

2.现在编写merge_sort_1,它计算开始和结束间隔的中点,并为每个半部分递归调用自身。在此之后,它调用merge来合并这两个半部分。你现在写的合并过程将是永久数组和临时数组、开始、中点和结束的函数。它在临时数组中维护一个索引,并在永久数组的每个(已排序)半部分中维护索引ij。它需要从开始到结束遍历临时数组,从永久数组的较低半部分或较高半部分中复制一个值。如果较低半部分中的i的值小于或等于较高半部分中的j的值,则选择较低半部分中的i的值,并运行i。如果较高半部分中的值j小于较低半部分中的值,则选择较高半部分中的j的值,并运行j。在使用一个部分的永久数组后,确保复制另一个部分的其余部分。此处教科书使用一个无限值 ∞ 技巧来避免检查两个部分是否被使用完。然而,这个技巧很难应用于这里,因为你应该把它放在哪里呢?最后,将临时数组中从开始到结束的所有值都复制回永久数组中。

第二个让我困惑,因为我不知道merge_sort_1应该做什么,为什么它必须与merge_sort不同。我也不知道为什么它需要传递起始和结束索引。实际上,也许我读错了什么,但说明听起来像merge_sort_1不做任何实际的工作。

此外,整个作业很困惑,因为我没有看到从说明中拆分原始数组以制作两个半部分。或者我是否误解了归并排序的思想?

希望我表述的有些意义。谢谢大家!

原文链接 https://stackoverflow.com/questions/4103769

点赞
stackoverflow用户258065
stackoverflow用户258065

首先,我会确保你理解归并排序。

看看这里的解释,里面有漂亮的动画帮助你理解它。

这是他们的伪代码版本:

#分割成两半
m = n / 2

#递归排序
sort a [1 .. m]
sort a [m + 1..n]

#使用临时数组合并排序后的子数组
b = a [1..m]的副本
i = 1,j = m + 1,k = 1
while i <= m and j <= n,
    a [k ++] =(a [j] <b [i])? a [j ++]:b [i ++]
    →不变式:a [1..k]处于最终位置
while i <= m,
    a [k ++] = b [i ++]
    →不变式:a [1..k]处于最终位置

看看他们如何使用b来保存一份数据的临时副本? 你的老师想要的是你传入一个表来用于这个临时存储。

这样清楚了这个任务吗?

2010-11-05 05:41:40
stackoverflow用户180090
stackoverflow用户180090

你的主要排序程序看起来像这样:(抱歉,我不知道 Lua,所以我会写一些类似 Java 的代码)

void merge_sort(int[] array) {
  int[] t = ...分配临时数组...
  merge_sort_1(array, 0, array.length, t);
}

merge_sort_1 接受要排序的数组,一些起始和结束索引,以及用于一些临时空间的数组。它执行实际的分治调用和调用 merge 程序。请注意,递归调用需要调用 merge_sort_1 而不是 merge_sort,因为您不希望在每个递归级别上分配数组,而是只需在归并排序过程的开始处分配一次。(这就是将归并排序分成两个程序的全部意义。)

我会留给你编写一个 merge 程序。它应该接受包含 2 个已排序子部分的原始数组和一个临时数组,并对原始数组进行排序。最简单的方法是将其合并到临时数组中,然后在完成时只需将其复制回来。

2010-11-06 00:14:07
stackoverflow用户173806
stackoverflow用户173806

首先,好吧,它是如何完成的?您是否只需在每个拆分和合并的递归中重写临时数组?

是的,临时数组会不断被覆盖。在合并阶段,临时数组用于保存合并结果,然后在合并结束时将其复制回永久数组中。

第二个问题很困惑,因为我不知道 merge_sort_1 应该做什么,为什么它必须是与 merge_sort 不同的方法。

merge_sort_1 是递归归并排序的递归中心。merge_sort 将作为一个方便的函数,创建临时数组并填充初始的开始和结束位置。

我也不知道为什么它需要传递起始和终止索引。实际上,也许我误读了一些东西,但是这些说明听起来好像 merge_sort_1 并没有做任何实际工作。

此外,整个任务很困惑,因为我从说明中看不出在哪里进行分割以使原始数组分为两半。或者我是否误解了归并排序?

递归函数 merge_sort_1 仅处理传入数组的一部分。它处理的部分由开始和结束索引定义。开始和结束之间的中点是分割数组的方式,然后在递归调用中再次分割。在完成上半部分和下半部分的递归调用后,将两个半部分合并到临时数组中,然后将其复制回永久数组中。

我能够按照描述写出 Lua 版本的归并排序,并可以对我的实现进行评论。似乎说明像是对教师实现的注释或评论。

这是 merge_sort 函数。正如我所说,它只是一个方便的函数,我认为它不是问题的关键。

-- Write a top level procedure merge_sort that takes as its argument
-- the array to sort.
function merge_sort(a)
    -- It should declare a temporary array and then call merge_sort_1,
    -- a procedure of four arguments: The array to sort, the one to use
    -- as temporary space, and the start and finish indexes within which
    -- this call to merge_sort_1 should work.
    merge_sort_1(a,{},1,#a)
end
2010-11-06 03:15:01