繁体   English   中英

迭代,就地归并排序

[英]iterative, In-place MergeSort

对于我的算法课,我们的任务是编写一个迭代而不是递归的合并排序实现,并且就地而不是需要另一个数组。 由于这是一个班级,我不想给我任何代码,但我无法弄清楚要做什么的基本算法。 除了代码或我不明白的解释之外,谷歌搜索不提供任何内容,因此这没有帮助。

我目前理解要做的是对大小为 1 的所有子数组进行排序(这当然是微不足道的),然后合并大小为 2、大小为 4 等的子数组,但这似乎更接近插入排序,然后有是如何使用恒定量的额外空间的问题。

最后一点,我不允许使用任何 C++ 标准库函数或类,例如向量、堆栈、任何种类等。

使用“某种”合并排序对数组进行就地排序的迭代算法可能如下所示。

我们以这个未排序的数组为例:

4, 3, 8, 5, 9, 2, 5, 1, 7, 10, 8, 0, 3

该算法将对合并数组的大小进行外循环。 所以一开始这个大小是1(还没有合并)。 然后在每次迭代中,这个大小加倍,这实际上将两个已经排序的连续段合并在一起。

实际的合并将在要合并的段中使用两个索引:一个位于要合并的两个段中的每一个的开头。

只要左侧索引处的值小于或等于另一个值,左侧索引就会递增(向右移动)。 在另一种情况下,执行此算法中最复杂的操作:

两个索引之间的值向右移动,最右边的值(第二个索引处的值)移动到第一个索引。 因此,值围绕一个位置循环。 在这个循环之后,两个索引都会增加。

重复此过程,直到左索引到达右索引,或者右索引到达第二段的末尾(可以是数组的末尾)。 当这种情况发生时,合并就完成了,在外循环的下一次迭代中,这两个段将被视为一个段。

以下是一些图像,说明了将在示例数据上执行的这些步骤:

在此处输入图片说明

此处执行合并以生成大小为 2 的段。 有时最后一个段不会有完整的大小,但这不是问题。 对于每个彩色段,放置了两个索引,一个指向两个值中的第一个,另一个指向第二个值。 如果第一个值大于第二个值,则交换它们。 在交换的情况下,两个索引都会增加,第二个索引到达第二个段的末尾(只有 1 个值宽),因此合并结束。 如果没有交换发生,只有第一个索引增加,但到达第二个索引,因此合并也结束(没有进行更改)。

在外循环的第二次迭代中变得更有趣:

在此处输入图片说明

要加入的第一段是 [3 4] 和 [5 8]。 两个索引分别指向 3 和 5(蓝色下划线)。 只要对应的值不大于第二个索引处的值,左索引就会递增。 在这种情况下,这意味着第一个索引到达第二个索引而没有任何更改。 对于第二对段,还有更多工作要做:

在此处输入图片说明

现在需要合并 [2 9] 和 [1 5]。 由于 2 大于 1,循环操作开始:1 必须移入,将 2 和 9 向右推一个位置。 两个索引都递增。 现在 2 不大于另一个值 (5),因此只增加第一个索引。 最后,9大于5,所以需要对它们进行交换,然后对这些段完成合并。

对下一对段执行类似的操作序列:

在此处输入图片说明

最后的“对”段实际上没有第二个段:第二个索引指向数组末尾之外,因此合并立即停止。

外循环再次迭代,将段大小加倍。 现在必须合并以下内容:

在此处输入图片说明

请注意 1(在第二个索引处)是如何在 [3 4 5 8] 之前移入的,这些都将向右移动一个位置以为其腾出空间。 2 也会发生同样的情况:它再次移入相同的 4 个值之前。 但是后来我们发现3不大于5,第一个索引递增,直到它指向8。5在8之前被注入。最后,8不大于9,所以第二个索引到达终点。

我不会为其他段和外循环的最终迭代提供相同的内容,这将再进行一次合并。

正如你所要求的,我没有提供代码;-)

一些注意事项

尽管这可以称为归并排序,但在最坏的情况下,值的循环确实会降低原始算法的效率。 确实,比较的次数仍然相同,但移动的次数可以更多。 以值 [3 4 5 8] 为例,它们移动了两次,为移动 1 和移动 2 腾出空间。这已经总计 10 次移动(并且该步骤中的合并尚未完成),而原始合并排序将始终进行与正在合并的段中的值相同数量的移动。 在更好的情况下,该算法根本不需要移动,或者比原始算法更少。

这种方法保证了稳定的排序。

你提到了恒定的空间和位置。

如果就地归并排序不必稳定,您可以交换元素而不是在数组之间移动它们。 对数组的左半部分进行归并排序到数组的右半部分,将合并后的输出与数组的右半部分交换,这样合并后的输出最终会出现在数组的右半部分,以及里面的内容数组的右半部分最终交换到数组的左半部分,重新​​排序(这是不稳定的部分)但未排序。

将第 2 季度合并排序到数组的第 1 季度,以便第 1 季度以排序数据结束,第 2 季度以重新排序但未排序的数据结束。 然后将数组的第 1 季度和第 2 部分合并到从第 2 季度开始的数组中,并在合并操作期间再次交换。 数组的最后 3/4 以排序结束,第一个 1/4 数组重新排序且未排序。

将第 2 个第 8 位合并到第 1 个第 8 位,然后将第 1 个第 8 位与数组的最后 3/4 合并排序,最后 7/8 位已排序,前 1/8 位已重新排序但未排序。

继续这样做,直到左侧只有一两个未排序的元素。 这些可以放置到位,对一个或两个元素进行部分插入排序。


如果使用常量空间并且归并排序需要稳定,可以使用自底向上归并排序,将数组的一部分移入常量空间,合并到数组中现在释放的空间中,然后重新打包数组填充一个或合并过程留下的两个间隙,并重复直到到达数组的最后剩余的释放部分,此时您可以使用常量空间和数组的释放部分进行正常的合并排序。 这样就完成了一次归并排序。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM