例如,给定一个整数数组及其两个连续序列的起始位置'b1'和'b2',还提供了位置'last',表示第二个序列的结束位置。从数组[b1]到数组[b2-1]以及从数组[b2]到数组[last]都是分别按顺序,如何使用O(n)时间和O(1)空间将它们合并到位 费用?
答案 0 :(得分:24)
Kronrod的合并是第一个公布的算法。它大致如下:
将数组的两个部分拆分为大小为k = sqrt(n)的块。使用第一个元素作为比较基础对块进行排序。这可以通过选择排序在sqrt(n)^ 2 = O(n)中完成。这里选择排序的关键属性是每个块有不断的移动,所以只有#comparisons是方形的。
在此阶段之后,对于数组中的每个元素A[i]
,其下方至多有k-1
个元素“错误地排序”,即j
位置的元素<{1这样i
。这些(可能)位于其下方最近的区块中,来自另一个合并的区域。请注意,块的第一个元素(以及它下面的所有其他块)已经相对于A[j]>A[i]
正确排序,因为块在它们的第一个元素上排序。这就是第二阶段工作的原因,即实现完全排序的数组:
现在将第一个块与第二个块合并,然后将第二个块与第三个块合并,等等,使用最后2个块作为合并输出的临时空间。这将扰乱最后两个块的内容,但在最后阶段,它们(与前一个块一起)可以按sqrt(n)^ 2 = O(n)时间中的选择排序进行排序。
答案 1 :(得分:22)
这绝不是一个简单的问题这是可能的,但很少在实践中完成,因为它比使用N-scratch空间的标准合并复杂得多。自{80}年代以来,Huang and Langston's论文一直存在,尽管实际的实施直到后来才真正浮出水面。早些时候,L。Trabb-Prado在1977年的论文明显早于黄和兰斯顿,但我很想找到纸的确切文本;只有参考文献比比皆是。
Geert,Katajainenb和Pasanen的优秀后期出版物Asymptotically efficient in-place merging(1995)是对多种算法的良好报道,并参考了Trabb-Prado对该主题的贡献。
答案 2 :(得分:10)
有真正的就地合并这样的东西,但它们并不简单,以至于任何人都不会在采访过程中独立地重新发明它们 - 已有多篇论文描述了一系列相当复杂的算法。 。一个是1988年3月CACM的Huang和Langston的实际就地合并。最初的想法是将长度为n的数据划分为大小为sqrt(n)的块,并使用一个块,填充最大的元素数据,提供用于合并其他数据的缓冲区空间。该论文的介绍说
“给定两个长度总和为n的排序列表,在O(n)步骤中合并的明显方法也需要线性数量的额外内存。另一方面,很容易将合并到位通过堆排序仅使用一定量的额外空间,但代价为O(n log n)时间“
因此,我声称可以完成真正的合并,但不是很明显。
答案 3 :(得分:0)
虽然完全不可能在O(n)
时间内完成,但我有一个命题要比O(n^2)
更快地完成它。我只使用O(1)
空格,这在我的代码中是临时的。我确信它应该比O(n^2)
更好。
private static int[] mergeSortedArrays(int[] a1, int[] a2) {
int i = 0, j = 0;
while (a1[i] != Integer.MIN_VALUE) {
if (a1[i] > a2[j]) {
int temp = a1[i];
a1[i] = a2[j];
a2[j] = temp;
for (int k = 1; k < a2.length; k++) {
if (a2[k - 1] > a2[k]) {
temp = a2[k - 1];
a2[k - 1] = a2[k];
a2[k] = temp;
}
}
}
i++;
}
while(j < a2.length){
a1[i++] = a2[j++];
}
return a1;
}
答案 4 :(得分:0)
这是O(n-1)个记忆(n + 1)
/**
* Created by deian on 2016-12-22.
* We just need track the two smallest numbers
*/
public class Merge {
public static void swap(int[] a, int i1, int i2) {
int t = a[i1];
a[i1] = a[i2];
a[i2] = t;
}
public static void merge(int[] a) {
// i1 and i2 - always point to the smallest known numbers
// it would works as well with two m and n sized arrays
int i1 = 0;
int i2 = a.length / 2;
System.out.printf(" %s, i(%d,%d) \n", Arrays.toString(a), i1, i2);
for (int di = 0; di < a.length - 1; di++) {
int ni;
int oi1 = i1; int oi2 = i2;
if (a[i1] > a[i2]) {
ni = i2; i2++;
if (i2 >= a.length) { i2--; }
} else {
ni = i1; i1++;
if (i1 >= i2) { i1 = di; }
}
if (di == i1) { i1 = ni; }
swap(a, di, ni);
System.out.printf("#%d: %s, i(%d,%d)s(%d>%d)i(%d,%d) \n", di + 1, Arrays.toString(a), oi1, oi2, ni, di, i1, i2);
}
System.out.printf(" %s\n", Arrays.toString(a));
}
public static void main(String[] args) {
// int[] a = new int[]{1, 3, 6, 8, -5, -2, 3, 8};
// int[] a = new int[]{1, 3, 6, 8, -5, 2, 3, 8};
// int[] a = new int[]{1, 5, 6, 8, -5, 2, 3, 4};
// int[] a = new int[]{1, 5, 6, 8, -5, -2, -1, 4};
// int[] a = new int[]{ 1, 2, 3, 4, 5, 6, 7, 8};
// int[] a = new int[]{5, 6, 7, 8, 1, 2, 3, 4};
int[] a = new int[]{1, 3, 5, 7, 2, 4, 6, 8};
merge(a);
}
}
答案 5 :(得分:-5)
public static void main(String[] args) {
int A[] = { 1, 3, 5, 6, 9 };
int B[] = new int[12];
B[0] = 3;
B[1] = 6;
B[2] = 8;
B[3] = 10;
B[4] = 11;
B[5] = 13;
B[6] = 15;
mergeInB(A, B, 7);
for (int n : B)
System.out.print(n + " ");
}
/**
* @param a
* @param b - it will be modified
* @param j = length of b
*/
public static void mergeInB(int[] a, int[] b, int j) {
int i = a.length - 1, k;
j --;
for (k = b.length-1; k >= 0; k--) {
if (i >= 0 && j >= 0) {
if (a[i] > b[j]) {
b[k] = a[i];
i --;
}
else
{
b[k] = b[j];
j --;
}
}
else break;
}
while(i>=0 && k >=0) {
b[k] = a[i];
k --;
i --;
}
while(j>= 0 && k >=0) {
b[k] = b[j];
j--;
k--;
}
}