如何使用O(n)时间和O(1)空间成本合并两个排序的整数数组

时间:2010-01-24 06:52:22

标签: algorithm

例如,给定一个整数数组及其两个连续序列的起始位置'b1'和'b2',还提供了位置'last',表示第二个序列的结束位置。从数组[b1]到数组[b2-1]以及从数组[b2]到数组[last]都是分别按顺序,如何使用O(n)时间和O(1)空间将它们合并到位 费用?

6 个答案:

答案 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)

几个小时前,我接受了一次采访(与一家非常重要的公司合作)。 Java中有答案

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--;
     }
}