关于数组中的就地合并

Posted

技术标签:

【中文标题】关于数组中的就地合并【英文标题】:Regarding in-place merge in an array 【发布时间】:2011-03-18 04:16:03 【问题描述】:

我遇到了以下问题。

给定一个由 n 个元素组成的数组和一个整数 k,其中 k n。元素 a0...akak+1...an sub> 已经排序。给出一个在 O(n) 时间和 O(1) 空间内排序的算法。

在我看来,它似乎不能在 O(n) 时间和 O(1) 空间内完成。问题似乎真的是在询问如何就地进行合并排序的合并步骤。如果可能的话,合并排序不会以这种方式实现吗?我无法说服自己,需要一些意见。

【问题讨论】:

这个问题是否特别说明了合并排序?我知道可以就地合并排序,但不是在 O(n) 时间内(至少我从未听说过。) 不,它没有。我正在类比合并步骤。看起来确实很相似。 如果您发布了问题的确切措辞,那么它似乎与归并排序没有任何关系。对于预先排序的数组(即插入排序),存在 O(1) 空间和 O(n) 就地排序算法。 Mergesort 不是其中之一,众所周知,它不是其中之一,所以... 那么你将如何在 O(n) 时间内解决这个问题?什么想法?可能你没有得到问题这里是一个例子...... 1,3,5,8 和 2,4,6,9 .. 你所暗示的是一个完全预排序的数组,它不是我的问题的情况。无论如何,对已经排序的数组进行排序是没有意义的。 how to merge two sorted integer array in place using O(n) time and O(1) space cost 的可能重复项 【参考方案1】:

This 似乎表明可以在 O(lg^2 n) 空间中进行操作。我看不出如何证明不可能在常数空间中合并,但我也看不出如何去做。

编辑: 追逐参考资料,Knuth Vol 3 - 练习 5.5.3 说“L. Trabb-Pardo 的一个相当复杂的算法为这个问题提供了最好的答案:可以在 O(n) 时间内进行稳定的合并和在O(n lg n) 时间,对于固定数量的索引变量仅使用 O(lg n) 位辅助存储器。

更多references 我没有读过。感谢您提出一个有趣的问题。

进一步编辑: 本文声称 Huang 和 Langston 的文章有一个算法,可以在 O(m + n) 时间内合并大小为 m 和 n 的两个列表,因此您的问题的答案似乎是肯定的。不幸的是,我无法访问这篇文章,所以我必须相信二手信息。我不确定如何将这与 Knuth 的 Trabb-Pardo 算法是最优的声明相协调。如果我的生活依赖它,我会选择 Knuth。

我现在看到这个问题已经在 Stack Overflow question 和 number 之前被问过。我不忍心将其标记为重复。

黄B.-C.和 Langston M. A.,实用的就地合并,Comm。 ACM 31 (1988) 348-352

【讨论】:

你是对的。因为我是大学,所以我能够阅读这篇论文。尽管技术非常复杂,但这似乎是可能的。谢谢指点。 你可以在 CiteSeerX 找到这篇论文。 citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.22.8523 @Daniel 谢谢。我的谷歌搜索技能需要改进。【参考方案2】:

有几种算法可以做到这一点,但都不是很容易直觉。关键思想是使用数组的一部分作为缓冲区进行合并,然后使用此缓冲区作为辅助空间进行标准合并。如果您随后可以重新定位元素以使缓冲区元素位于正确的位置,那么您就是黄金。

如果您有兴趣查看其中一种算法,我已经在我的个人网站上写了an implementation。它基于 Huang 和 Langston 的论文“Practical In-Place Merging”。您可能需要查看该论文以获得一些见解。

我还听说对此有很好的自适应算法,它使用您选择的一些固定大小的缓冲区(如果您愿意,可以是 O(1)),然后随着缓冲区大小优雅地缩放。我不知道其中的任何一个,但我确信快速搜索“自适应合并”可能会有所收获。

【讨论】:

【参考方案3】:

不,这是不可能的,虽然如果是这样,我的工作会容易得多:)。

您有一个无法避免的 O(log n) 因素。您可以选择将其视为时间或空间,但避免它的唯一方法是不排序。使用 O(log n) 空间,您可以构建一个延续列表,以跟踪您将不太适合的元素隐藏在何处。通过递归,这可以适应 O(1) 堆,但这只能通过使用 O(log n) 堆栈帧来代替。

这是从 1 到 9 合并排序赔率和偶数的进度。请注意您如何需要日志空间记帐来跟踪由常量空间和线性交换的双重约束引起的顺序倒置。

. - 135792468 . - 135792468 : .- 125793468 : .- 123795468 #.:- 123495768 :.- 123459768 .:- 123456798 .- 123456789 123456789

有一些微妙的边界条件,比二分搜索稍微难于正确,即使是这种(可能的)形式,因此是一个糟糕的作业问题;但真的是很好的脑力锻炼。

更新 显然我错了,有一种算法可以提供 O(n) 时间和 O(1) 空间。我已经下载了论文来启发自己,并以不正确的方式撤回此答案。

【讨论】:

链表是可能的。 O(log n) 来自其他地方。 我可以看到如何使用 lg n 额外空间来做到这一点。我看不出如何证明你不能做得更好,即 O(lg n) 额外空间对于保持线性是必要的。 @Joshua。说链表可以实现是不公平的,因为链表有 O(n) 条额外的信息使其更容易——从元素到元素的指针。如果你能负担得起 O(n) 额外的空间,你可以用数组做同样的事情。您分配一个新的结果数组,然后遍历您的两个原始数组,按顺序复制项目。 @deinst 我并不觉得这很容易,但我最终证明了令我满意的是 lg n 是下限。那是几年前的事了,不幸的是我不再有它了。然而,证明无论下界是什么,它都比 O(1) 高,这对于我们这里的目的来说已经足够了。

以上是关于关于数组中的就地合并的主要内容,如果未能解决你的问题,请参考以下文章

c_cpp 就地,将数组中的副本移动到最后。

Java:就地对 ArrayList 进行排序

就地映射 NumPy 数组

如何就地反转 NumPy 数组?

就地修改数组并了解其内存分配

C# 就地将 `int[]` 数组转换为 `byte[]` 数组