Объединение двух массивов без использования дополнительного пространства - PullRequest
2 голосов
/ 05 февраля 2011

У меня есть 2 отсортированных массива, a1 и a2, длины l1 и l2 соответственно. Массив a2 имеет пустое место в конце длины l1, поэтому он может содержать все элементы a1 в дополнение к своим собственным элементам. Теперь я хочу объединить a1 в a2, чтобы a2 содержал все элементы a1 и a2 в отсортированном порядке. В идеале это должно использовать 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

Ответы [ 10 ]

9 голосов
/ 05 февраля 2011

Трудно сказать, что делает ваш код, но, похоже, он имеет субоптимальную (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 Вам нужно будет добавить обработку для случая, когда один из i и j отрицателен в цикле.Очевидно, что в этом случае должен быть скопирован другой элемент.

edit
Позже можно выполнить это условие

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

вместо

if (a1[i] >= a2[j]) {
4 голосов
/ 05 февраля 2011

Если элементы в a1 и a2 отсортированы, у вас будет что-то вроде этого:

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
2 голосов
/ 05 февраля 2011

Сначала переместите элементы a1 на заднюю часть a1. Второе слияние a1 и a2, начинающееся с фронта a1 (т. Е. Запишите минимум двух сравниваемых элементов с текущим индексом в a1, где текущий индекс начинается с 0 и находится в диапазоне a1.length + a2.length - 1) , Это предотвратит перезапись любых элементов a1.

1 голос
/ 05 февраля 2011

Есть много хороших ответов.Я просто хотел что-то добавить (комментарии уже так погребены):

Это просто фаза слияния сортировка слиянием или аналогичная, например k-way sort . Просто используйте процедуру слияния на месте.Либо «меньший массив», либо «пустое пространство» можно использовать для хранения значений в «большем массиве», которые в данный момент не находятся в порядке сортировки.

Можно заимствовать биты и куски различных алгоритмов:-)

1 голос
/ 05 февраля 2011

Я бы начал слияние с конца.

На последнем элементе поставить max(lastOf(a1), lastOf(f2)). Продолжайте откусывать по одному элементу за раз от остального массива, пока один из них не будет исчерпан. Поместите оставшуюся часть массива в начало (может, я не буду).

0 голосов
/ 02 февраля 2016

Это всего лишь фаза слияния сортировки слиянием,

  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<low>,3<mid>,7<high>);

вот алгоритм на 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));
}
0 голосов
/ 14 июля 2014

Объединение двух отсортированных массивов без использования дополнительной памяти

#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] << " " ;
}
0 голосов
/ 05 февраля 2011

Полагаю, у вас нет ограничений по сложности вашего алгоритма по времени.Я предлагаю добавить значения 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;
    }
0 голосов
/ 05 февраля 2011

Зачем писать свой собственный код?Если у a2 достаточно пустого места для элементов a1, просто скопируйте элементы из a1 в a2 (используя arraycopy), а затем используйте Arrays.sort ().Это дало бы вам O (n * log (n)), тогда как ваш простой подход в любом случае выглядит как O (n * n).

(Однако объединение может быть сделано в O (n).)

0 голосов
/ 05 февраля 2011

вы пробовали System.arrayCopy?Что-то вроде:

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

Должен делать свое дело, не теряя времени (arraycopy оптимизирован для этих случаев использования).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...