【问题标题】:Merging two arrays without using extra space合并两个数组而不使用额外空间
【发布时间】:2011-06-21 16:11:31
【问题描述】:

我有 2 个排序数组,a1a2,长度分别为 l1l2。数组a2 在长度l1 的末尾有空白空间,因此它可以包含a1 的所有元素以及它自己的元素。现在,我想将a1 合并到a2 中,这样a2 将按排序顺序包含a1a2 的所有元素。理想情况下,这应该使用 O(1) 辅助存储空间。我有以下代码,但出了点问题:

 public static int[] merge(int []a1,int a2[],int l1, int l2){

         System.out.println("l1 =" +l1 + " l2=" +l2);
         int es = l2-l1;
         int fs = l2-es;

         System.out.println("es= " +es);
         System.out.println("fs = " + fs);
         int j=0;

         for(int i=0;i< l1;i++){

             if(j<fs){
                // System.out.println("i= " + i + "a1[i]=" + a1[i]);
                // System.out.println("j= " + j + "a2[j]=" + a2[j]);
                 if(a1[i]<a2[j]){
                     add(a2,j,a1[i],l2);
                     //i++;
                     fs++;
                 }
             }else{
                 System.out.println("***");
                 a2[j]=a1[i];
             }

             j++;
         }

         return a2;
     }


    public static void add(int []a,int p,int key,int l){

        for(int i=l-1;i>p;i--){
              a[i]= a[i-1];
        }
        a[p]= key;
    }

有人对如何解决这个问题有任何想法吗?我使用以下数据来运行代码:

int a1[]= new int[]{-1,0,7,8};
int a2[]= new int[7];
a2[0]=1;
a2[1]=3;
a2[2]=9;

输出是

l1 =4 l2=7
es= 3
fs = 4
-1
0
1
3
9
0
0

【问题讨论】:

  • 你能解释一下那是什么东西吗?
  • 欢迎来到 Stack Overflow!您可以在代码中添加一些注释以阐明您要做什么吗?你能提供更多关于出了什么问题的细节吗?如果您让我们更深入地了解问题,那么帮助您会容易得多。
  • 旁注:在 Java 中,数组知道它们自己的长度,因此您可以用 a1.lengthl2 类似地替换 l1
  • 您的算法似乎在进行合并,方法是从前面开始工作,然后将第二个数组的元素向下移动以腾出更多空间。您是否考虑过从第二个数组的 back 开始向前面移动?您可以检查一下,如果您采用这种方法,您将永远不需要重新洗牌。
  • 看起来您在每一步中都在移动任意数量的元素。这使它成为O(n**2) 操作,因此合并没有意义,l1.addAll(l2); Collections.sort(l1); 会更好地为您服务。请参阅stackoverflow.com/questions/2571049/… - 编辑:我看到我忽略了您正在使用数组而不是列表。但是,它可以很容易地适应。

标签: java algorithm merge


【解决方案1】:

您尝试过 System.arrayCopy 吗?类似的东西:

...
System.arraycopy(a2, 0, a2, a1.length, a2.length);
System.arraycopy(a1, 0, a1, 0, a1.length);
...

应该在不浪费时间的情况下做到这一点(arraycopy 针对这些用例进行了优化)。

【讨论】:

  • 这将如何产生排序数组?
  • OP 并没有像 merge 那样尝试 复制 数组。这要求两个范围按排序顺序交错。
  • 哈,我的错...阅读“按该顺序排列的 a1 和 a2 的元素”...而不是“按排序顺序”。之后他仍然可以使用 arraycopy 和 Arrays.sort()。
【解决方案2】:

很难说出您的代码做了什么,但它似乎具有次优 (O(n^2)) 复杂性:add 方法内部还有第二个循环。
另外,请注意fs 始终等于l1

但是有更简单的方法:从后面。如果你想一想,总是有足够的空间。

类似的东西

int i = l1 - 1;
int j = l2 - 1;
int result_pos = l1 + l2 - 1;
while (i >= 0 || j >= 0) {
    if (a1[i] >= a2[j]) {
        a2[result_pos--] = a1[i--];
    } else {
        a2[result_pos--] = a2[j--];
    }
}

PS 当ij 之一在循环中为负时,您需要添加处理。显然,在这种情况下应该复制另一个元素。

编辑
以后可以用这个条件完成

if (j < 0 || (i >= 0 && a1[i] >= a2[j])) {

而不是

if (a1[i] >= a2[j]) {

【讨论】:

  • @algorithmX 不,我们从第二个数组的 end 开始写入数字。第二个数组的长度为l1 + l2。就像在 OP 的示例中一样,它是 4 + 3 = 7
【解决方案3】:

首先,将 a1 的元素移到 a1 的后面。第二次合并 a1 和 a2 从 a1 的前面开始(即,将要比较的两个元素中的最小值写入 a1 中的当前索引,其中当前索引从 0 开始,范围最大为 a1.length + a2.length - 1) .这将防止您覆盖 a1 的任何元素。

【讨论】:

    【解决方案4】:

    为什么要编写自己的代码?如果 a2 有足够的空间来容纳 a1 的元素,只需将 a1 中的元素复制到 a2 中(使用 arraycopy),然后使用 Arrays.sort()。这会给你 O(n*log(n)) 而你的简单方法似乎是 O(n*n) 反正。

    (但是合并可以在 O(n) 中完成。)

    【讨论】:

    • 他说的是内存复杂度,而不是操作复杂度。 Arrays.sort() 使用了多少内存(我不知道 TBH)?
    • 好点。我完全忽略了这里的内存复杂性。重点是表明操作复杂性会更高,代码复杂性也会更高。但是由于问题中提到了内存复杂性,因此我的回答应该已经解决了这个问题。根据我在文档中阅读的内容,Arrays.sort() 内部使用快速排序或合并排序,所以我猜内存消耗 O(log(n)) 或 O(n)。
    【解决方案5】:

    我会从头开始合并。

    在最后一个元素处,输入max(lastOf(a1), lastOf(f2))。继续从任一数组的其余部分中一次咬掉一个元素,直到其中一个元素耗尽。将剩余数组的其余部分放在开头(可能我没有操作)。

    【讨论】:

    • 不确定其他人的建议(因为他们没有给出想法,只是给出代码),但这是最简单和最快的方法。就速度而言,它与使用第三个数组一样好。
    【解决方案6】:

    如果a1a2 中的元素被排序,那么你会得到这样的结果:

    a1 : [-1] [0] [7] [8]
    a2 : [1] [3] [9] [] [] [] []
    

    所以在代码中你可以这样做:

    int a1i = 0; // pointer to the ith element in the array a1
    int tmp = 0;
    int i = 0;
    for(i = 0; i < a1.length; i++) {
        if(a2[i] > a1[a1i]) {
            tmp = a2[i];
            a2[i] = a1[a1i];
            a1[a1i] = tmp;
            Arrays.sort(a1); // This might take more memory though...
        } else {
            a1i++;
        }
    }
    a1i = 0;
    for(i; i < a2.length; i++) {
        a2[i] = a1[a1i];
        a1i++;
    }
    

    这样可以:

    a1 : [-1] [0] [7] [8]
          ^
    a2 : [1] [3] [9] [] [] [] []
          ^
    SWAP
    
    a1 : [1] [0] [7] [8]
          ^
    a2 : [-1] [3] [9] [] [] [] []
               ^
    SORT
    
    a1 : [0] [1] [7] [8]
          ^
    a2 : [-1] [3] [9] [] [] [] []
               ^
    SWAP
    
    a1 : [3] [1] [7] [8]
          ^
    a2 : [-1] [0] [9] [] [] [] []
                   ^
    SORT
    
    a1 : [1] [3] [7] [8]
          ^
    a2 : [-1] [0] [9] [] [] [] []
                   ^
    SWAP
    
    a1 : [9] [3] [7] [8]
          ^
    a2 : [-1] [0] [1] [] [] [] []
                      ^
    SORT
    
    a1 : [3] [7] [8] [9]
          ^
    a2 : [-1] [0] [1] [] [] [] []
                      ^
    COPY
    
    a1 : [3] [7] [8] [9]
              ^
    a2 : [-1] [0] [1] [3] [] [] []
                       ^
    COPY
    
    a1 : [3] [7] [8] [9]
                  ^
    a2 : [-1] [0] [1] [3] [7] [] []
                           ^
    COPY
    
    a1 : [3] [7] [8] [9]
                      ^
    a2 : [-1] [0] [1] [3] [7] [8] []
                               ^
    COPY
    
    a1 : [3] [7] [8] [9]
                      ^
    a2 : [-1] [0] [1] [3] [7] [8] [9]
                                   ^
    END
    

    【讨论】:

      【解决方案7】:

      我假设您对算法的时间复杂度没有限制。 我的建议是将 a1 的值附加到 a2 并应用任何 O(nlogn) 排序算法,如快速排序。但是,如果您想进行合并,我认为这段代码可以帮助您:

      public static void main(String[] args) {
              // TODO Auto-generated method stub
              int a1[]= new int[]{-1,0,7,8};
              int a2[]= new int[7];
              a2[0]=1;
              a2[1]=3;
              a2[2]=9;
              merge(a1, a2, 3);
          }
      
          private static void merge(int[] a1, int[] a2, int lastPos) {
              for ( int i = 0; i < a1.length; i++)
              {
                  for ( int j = 0; j < a2.length; j++)
                      if ( a1[i] < a2[j] )
                      {
                          add(a1[i], a2, j, lastPos);
                          lastPos++;
                          break; //since a2 is also sorted
                      }
              }
          }
      
          private static void add(int val, int[] a2, int j, int lastPos) {
              for ( int i = lastPos; i > j; i--)
                  a2[i] = a2[i-1];
              a2[j] = val;
          }
      

      【讨论】:

      • 数组开始按问题排序。
      • 我的测试和原始问题中描述的一样,两个子数组都是排序的 (-1,0,7,8) 和 (1,3,9,0,0,0, 0)。尾随零用于最终结果。这就是为什么我的函数获取第二个数组 (3) 的大小。
      【解决方案8】:

      有很多好的答案。我只是想添加一些东西(cmets已经被埋没了):

      这只是merge-sort 或类似k-way sort 的合并阶段。只需使用就地合并例程即可。 “较小的数组”或“空白空间”都可用于存储当前未按排序顺序的“较大数组”中的值。

      借用一点点不同的算法也没关系 :-)

      【讨论】:

        【解决方案9】:

        在不使用额外内存的情况下合并两个排序数组

        #include<iostream>
        using namespace std ;
        const int N = 100 ;
        int a[N] , b[N] , n , m , len , L ;
        int main() {
            cin >> n ;
            for( int i = 0 ; i < n ; i++ ) cin >> a[i] ;
            cin >> m ;
            for( int i = 0 ; i < m ; i++ ) cin >> b[i] ;
            len = n + m - 1 ;
            L = n + m ;
            n--  , m-- ;
            while( len >= 0 ) {
                if( m < 0 ) a[len] = a[n--];
                else if( n < 0 ) a[len] = b[m--];
                else if( a[n] > b[m] ) a[len] = a[n--];
                else a[len] = b[m--];
                len--;
            }
            for( int i = 0 ; i < L ; i++ ) cout << a[i] << " " ;
        }
        

        【讨论】:

          【解决方案10】:

          这不过是归并排序的合并阶段,

          1. 将a2的所有元素(1,2,3,4)复制到a1的末尾(5,6,7,8),现在a1将包含(4,5,6,7,8,1,2,3,4)
          2. 现在调用inPlaceMerge(collection, 0&lt;low&gt;,3&lt;mid&gt;,7&lt;high&gt;);下面的合并算法

          这是Java中的算法,

          public static <T extends Comparable<? super T>>  void inPlaceMerge(T[] collection, int low, int mid, int high) {
              int left = low;
              int right = mid + 1;
          
              if(collection[mid].equals(collection[right])) {
                  return ;//Skip the merge if required
              }
              while (left <= mid && right <= high) {          
                  // Select from left:  no change, just advance left
                  if (collection[left].compareTo(collection[right]) <= 0) {
                      left ++;
                  } else { // Select from right:  rotate [left..right] and correct
                      T tmp = collection[right]; // Will move to [left]
                      rotateRight(collection, left, right - left);
                      collection[left] = tmp;
                      // EVERYTHING has moved up by one
                      left ++; right ++; mid ++;
                  }
              }       
          }
          
          private static <T extends Comparable<? super T>> void rotateRight(T[] collection, int left, int numberOfElements) {
              System.arraycopy(collection, left, collection, left+1, numberOfElements);       
          }
          

          这是单元测试

          @Test
          public void inPlaceMergeFirstTest() {
              Integer[] collection = new Integer[]{5,6,7,8,1,2,3,4};
              ArrayUtils.<Integer>inPlaceMerge(collection, 0,3,7);
              Integer[] result = new Integer[]{1,2,3,4,5,6,7,8};
              assertThat(collection, equalTo(result));
          }
          

          【讨论】:

            【解决方案11】:

            解决问题的更好方法是使用插入排序来合并 O(1) 辅助空间中的两个排序数组。

            /* 因为我们必须合并 array2 中的 2 个排序数组。我们从 array2 的末尾开始,将数组元素插入到其正确的位置 acc。插入排序*/

            public static int[] merge(int []a1,int a2[],int l1, int l2){
            
                 int len2=l2-l1;
                  for(int i=l1-1;i>=0;i--){
                    int j=len2-1;
            
                      for(;j>=0 && j+1<l2 && a2[j]>a[i];j--){
                         a2[j+1]=a2[j];
                       }
                      a2[j+1]=a1[i];
                        len2++;
                     }
            }
            

            【讨论】:

              【解决方案12】:

              /或者你可以这样做->/

              public static int[] merge(int []a1,int a2[],int l1, int l2){
                   int j=0;
                   for(int i=l2-l1;i<l2;i++)
                   {
                          a2[i]=a1[j];
                            j++;
                    }
                    Arrays.sort(a2);
                    return a2;
              
               }
              

              【讨论】:

                猜你喜欢
                • 2017-01-08
                • 1970-01-01
                • 2013-08-30
                • 2011-10-26
                • 1970-01-01
                • 1970-01-01
                • 2021-07-01
                • 2021-12-22
                • 2020-03-25
                相关资源
                最近更新 更多