如何在Big-O(N)时间内将3个排序数组合并为1个排序数组?

时间:2016-06-21 03:41:21

标签: java algorithm sorting data-structures big-o

尝试将3个数组合并为一个,以便最终数组按顺序排列。

给出

int[] a = {1,3};
int[] b = {2,4};
int[] c = {1,5};

合并数组,使最终数组d = {1,1,2,3,4,5}

不能连接它们然后对d数组进行排序,因为这会使时间复杂度大于Big-O(N)。

这是我到目前为止所得到的。索引超出限制异常时遇到问题:

public static void main(String[] args) {
    // Sort these 3 arrays. The final array should be d = {1,1,2,3,4,5}
    int[] a = {1,3};
    int[] b = {2,4};
    int[] c = {1,5};
    int[] d = new int[a.length + b.length + c.length];

    int i = 0;
    int j = 0;
    int k = 0;
    int l = 0;

    for (int iteration = 0; iteration <= d.length; iteration++){
        if ((i != a.length || j != b.length) && a[i] < b[j]){
            if (a[i] < c[k]){
                // then a[i] is smallest
                d[l] = a[i];
                i++;
                l++;
                displayArrayContents(a,b,c,d,i,j,k,l);
            }
            else if (a[i] > c[k]){
                // then c[k] is smallest
                d[l] = c[k];
                k++;
                l++;
                displayArrayContents(a,b,c,d,i,j,k,l);
            }
            else if (a[i] == c[k]){
                d[l] = a[i];
                i++;
                l++;
                d[l] = c[k];
                k++;
                l++;
                displayArrayContents(a,b,c,d,i,j,k,l);
            }
        }
        else if(b[j] < a[i]){
            if (b[j] < c[k]){
                // b[j] is smallest
                d[l] = b[j];
                l++;
                j++;
                displayArrayContents(a,b,c,d,i,j,k,l);
            }
            else if (b[j] > c[k]){
                // c[k] is smallest
                d[l] = c[k];
                l++;
                k++;
                displayArrayContents(a,b,c,d,i,j,k,l);
            }
            else if (b[j] == c[k]){
                d[l] = b[j];
                j++;
                l++;
                d[l] = c[k];
                k++;
                l++;
                displayArrayContents(a,b,c,d,i,j,k,l);
            }
        }
    }
}

4 个答案:

答案 0 :(得分:4)

您的想法是正确的,代表 O(n)解决方案。但是,您的代码确实存在一些问题,其中一些问题会导致越界异常:

  • 您无需先确认c[k];
  • 即可访问k < c.length
  • 即使您在length进行测试,也是以无法避免此类无效访问的方式执行此操作:(i != a.length || j != b.length) && a[i] < b[j]仍会导致a[i]i === a.length(特别是j != b.length);
  • 时访问
  • 外部循环需要迭代的次数通常是错误的,因为有时(在相等的情况下)您在目标数组中存储了两个值,这使得数组填充的速度比循环预测的要快。事实上,平等的情况(如a[i] == c[k])并不需要单独处理。如果将它与>一起处理(所以:>=),算法仍然正确:第二个(相等)值将在下一次迭代中复制;
  • 即使您修复了上一个问题,您的外部循环仍会使一次迭代过多; for条件应为< d.length而非<= d.length

没有问题,但您的代码中有很多重复:

  • 你可以将调用移到displayArrayContents(a,b,c,d,i,j,k,l);构造之外的if,因此它总是被执行,这是你真正想要的;
  • 由于您始终在d构造中分配给if,因此您可以使用三元运算符if将该分配置于“? ... :”之外;
  • 虽然像i != a.length这样的测试可以达到预期目的,但最好是按照以下方式进行测试:i < a.length

以下是考虑到上述代码的代码:

import java.util.Arrays; // for easy output of arrays with Arrays.toString().

class Main {
  public static void main(String[] args) {
    // Sort these 3 arrays. The final array should be d = {1,1,2,3,4,5}
    int[] a = {1,3};
    int[] b = {2,4};
    int[] c = {1,5};
    int[] d = new int[a.length + b.length + c.length];

    int i = 0;
    int j = 0;
    int k = 0;
    for (int l = 0; l < d.length; l++) {
      d[l] = i < a.length && (j >= b.length || a[i] < b[j])
                ? (k >= c.length || a[i] < c[k]
                    ? a[i++]
                    : c[k++])
                : (j < b.length && (k >= c.length || b[j] < c[k])
                    ? b[j++]
                    : c[k++]);
       // Uncomment this if you still need it:
       //displayArrayContents(a,b,c,d,i,j,k,l); 
    }

    System.out.println(Arrays.toString(d));
  }
}

最后一项陈述的输出:

[1, 1, 2, 3, 4, 5]

repl.it上看到它。

答案 1 :(得分:2)

请按照以下步骤操作:

  • 从此处获取答案代码:How to merge two sorted arrays into a sorted array?
  • ab上调用该函数,以获取生成的数组ab
  • abc上调用该功能,以获得结果abc
  • 您已经两次调用O(n)函数,因此它仍然是O(n)。 BOOM。

事实是,玩数组索引令人沮丧。如果您可以将这些数组替换为队列或Itererator,只需take()next()每次迭代中的最小值并将其放在结果列表中,它就会更清晰。

答案 2 :(得分:0)

你需要清楚N的变化。如果你总是只有三个数组,它们的大小或最大大小随N变化,那么几乎所有重复选择三个数组中任何一个数组的代码的代码,删除它,并将其追加到结果数组的末尾,将是O(N)。您选择最小数字的代码可能很笨拙而且价格昂贵,但它只是一个不变因素,随着N的增加不会改变。

如果要合并的数组数量随N增加,那么您需要更加小心选择可用的最小数字,最终会出现排序问题,在线性时间内无法做到通常的假设。

通常,外部排序会使用堆(例如http://www.geeksforgeeks.org/external-sorting/)合并磁盘上保存的大量列表。这对于一次合并大量列表会更有效,但只是为了获得一个常数因子,

答案 3 :(得分:0)

假设这是java,数组名称是对数组的引用,可以像C / C ++中的指针一样交换。这可以用于减少主合并循环中的条件数量,使代码更简单,但代价是交换。在主合并循环之前完成空数组检查。这种方法可以很容易地扩展到处理4路或更大的合并,否则需要很多条件。

static int[] Merge(int[] a, int[] b, int[] c)
{
    int[] d = new int[a.length + b.length + c.length];
    int[] e;   // temp used for swap
    int i = 0;
    int j = 0;
    int k = 0;
    int l = 0;
    int t;
    // empty array checks
    if(0 == b.length){      // if b empty
        if(0 == c.length){  // if b and c empty
            c = a;          //   c = a
            a = b;          //   a = b = empty
        } else {            // if b empty, c not empty
            e = a;          //   swap a and b
            a = b;
            b = e;
        }
    } else {                // else b not empty
        if(0 == c.length){  // if c empty
            e = c;
            c = b;          //   shift c = b, b = a
            b = a;
            a = e;          //   a = empty
        }
    }
    // main merge loop
    while(i < a.length){    // 3 way merge
        if(a[i] > b[j]){    // if b smaller swap
            e = a;
            a = b;
            b = e;
            t = i;
            i = j;
            j = t;
        }
        if(a[i] > c[k]){    // if c smaller swap
            e = a;
            a = c;
            c = e;
            t = i;
            i = k;
            k = t;
        }
        d[l++] = a[i++];
    }
    while(j < b.length){    // 2 way merge
        if(b[j] > c[k]){    // if c smaller swap
            e = b;
            b = c;
            c = e;
            t = j;
            j = k;
            k = t;
        }
        d[l++] = b[j++];
    }
    while(k < c.length)     // copy rest of c
        d[l++] = c[k++];
    return d;
}