迭代,就地MergeSort

时间:2017-10-11 19:18:36

标签: c++ algorithm sorting mergesort

对于我的算法类,我们的任务是编写一个迭代而不是递归的合并排序实现,而不是需要另一个数组。由于这是一个类,我不希望给我任何代码,但我无法弄清楚该怎么做的基本算法。谷歌搜索除了代码或者我不理解的解释之外不提供任何内容,因此这不是一个帮助。

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

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

2 个答案:

答案 0 :(得分:1)

使用"类型"的迭代算法合并排序以对数组进行排序就地可能如下所示。

让我们把这个未排序的数组作为例子:

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

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

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

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

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

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

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

enter image description here

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

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

enter image description here

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

enter image description here

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

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

enter image description here

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

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

enter image description here

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

我不会为其他段提供相同的内容,以及外部循环的最后一次迭代,它将再进行一次合并。

根据您的要求,我不提供代码; - )

一些注意事项

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

此方法可确保稳定排序。

答案 1 :(得分:0)

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

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

将第二季度合并到阵列的第一季度,以便第一季度最终得到排序数据,第二季度最终得到重新排序但未排序的数据。然后在第二季度开始时将第一季度和阵列的第二半合并到阵列中,再次在合并操作期间进行交换。数组的最后3 / 4s最终排序,第一个1/4数组被重新排序和未排序。

合并排序第二个第八个到第八个第八个,然后合并排序第一个第八个和最后一个3/4的数组,最后是最后一个7/8排序和第一个1/8重新排序但未排序。

继续此操作,直到左侧只有一个或两个未分类的元素。这些可以用于对一个或两个元素进行部分插入排序。

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