2

I have 2 sorted arrays, a1 and a2, of lengths l1 and l2, respectively. The array a2 has empty space at the end of length l1, so it can hold all of the elements of a1 in addition to its own elements. Now, I want to merge a1 into a2 so that a2 will contain all the elements of a1 and a2 in sorted order. Ideally this should use O(1) auxiliary storage space. I have the following cod,e but something is going wrong:

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

Does anyone have any ideas on how to fix this? I used following data to run the code:

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

Output is

l1 =4 l2=7
es= 3
fs = 4
-1
0
1
3
9
0
0
templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
algorithmX
  • 91
  • 1
  • 1
  • 6
  • 3
    Can you clarify what that something is? – unholysampler Feb 04 '11 at 22:36
  • Welcome to Stack Overflow! Can you add some commenting to your code to clarify what you're trying to do? And can you provide a few more details on what's going wrong? It will be a lot easier to help you out if you give us more insight into the problem. – templatetypedef Feb 04 '11 at 22:36
  • 3
    Side note: in Java the arrays know their own length so you can replace `l1` with `a1.length` and `l2` similarly. – Péter Török Feb 04 '11 at 22:41
  • 3
    Your algorithm seems to be doing the merge by working from the front forward and shuffling down the elements of the second array to make more space. Have you considered instead starting from the *back* of the second array and moving toward the front? You can check this to see that you will never end up needing to shuffle elements out of the way if you take this approach. – templatetypedef Feb 04 '11 at 22:46
  • It looks like you're shifting an arbitrary amount of elements in each step. This makes it to an `O(n**2)` operation, so the merging makes no sense and you'd be better served by `l1.addAll(l2); Collections.sort(l1);`. See http://stackoverflow.com/questions/2571049/how-to-sort-in-place-using-the-merge-sort-algorithm - EDIT: I see I overlooked you're working with arrays instead of lists. However, it can be easily adapted. – maaartinus Feb 04 '11 at 22:51

13 Answers13

9

It's difficult to tell what your code does, but it seems to have suboptimal (O(n^2)) complexity: there's a second loop inside add method.
Also, note that fs is always equal to l1.

But there's much simpler method: from the back. If you think about it, there's always enough space.

Something like this

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 You'll need to add handling for the case when one of i and j is negative in the loop. Obviously, in this case another element should be copied.

edit
Later can be done with this condition

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

instead of

if (a1[i] >= a2[j]) {
Nikita Rybak
  • 67,365
  • 22
  • 157
  • 181
  • @algorithmX No, we start writing numbers from the _end_ of second array. And second array has length `l1 + l2`. Like in OP's example, it's `4 + 3 = 7`. – Nikita Rybak Feb 05 '11 at 00:10
4

If the elements in a1 and a2 are sorted then you'd have something like this:

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

So in code you can do this:

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

This would work out to:

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
Argote
  • 2,155
  • 1
  • 15
  • 20
2

First, shift the elements of a1 to the back of a1. Second merge a1 and a2 starting the front of a1 (i.e., write the minimum of the two elements being compared to the current index in a1, where the current index starts at 0 and ranges up to a1.length + a2.length - 1). This will prevent you from overwriting any elements of a1.

jonderry
  • 23,013
  • 32
  • 104
  • 171
1

I'd start merging from the end.

At the last element, put max(lastOf(a1), lastOf(f2)). Continue to bite off one element at a time from the rest of either array, until one of these is exhausted. Put the rest of the remaining array to the start (may me a no-op).

9000
  • 39,899
  • 9
  • 66
  • 104
  • Not sure what other people are suggesting (as they are not giving the idea, just the code), but this is the simplest and fastest way to do so. It is as good as using a 3rd array in terms of speed. – rents Sep 24 '15 at 09:54
1

There are many good answers. I just wanted to add something (the comments are already so buried):

This is just the merging phase of a merge-sort or similar such as a k-way sort. Just use an in-place merge routine. Either the "smaller array" or the "empty space" can be used to store values in the "larger array" which are not currently in sort-order.

It's okay to borrow bits and pieces of different algorithms :-)

1

The better way to solve the question is used to Insertion sort for merging two sorted arrays in O(1) auxiliary space.

/* as we have to merge the 2 sorted arrays in array2. we begin from the end of array2 and insert the array element at its correct position acc. to insertion sort*/

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++;
         }
}
Martin Gal
  • 16,640
  • 5
  • 21
  • 39
0

Merge Two Sorted Array Without Using Extra Memory

#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] << " " ;
}
Rezwan4029
  • 109
  • 3
0

This is nothing but merge phase of merge sort,

  1. copy all the elements of a2 (1,2,3,4) at the end of a1 (5,6,7,8), now a1 will contain (4,5,6,7,8,1,2,3,4)
  2. Now invoke the merge algorithm below inPlaceMerge(collection, 0<low>,3<mid>,7<high>);

here is the algorithm in 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);       
}

Here is the unit test

@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));
}
craftsmannadeem
  • 2,665
  • 26
  • 22
0

Why bother writing your own code? If a2 has enough empty space to hold the elements of a1, just copy tha elements from a1 into a2 (using arraycopy) and then use Arrays.sort(). That would give you O(n*log(n)) whereas your simple approach seems to be O(n*n) anyway.

(However the merge could be done in O(n).)

Axel
  • 13,939
  • 5
  • 50
  • 79
  • He's talking about memory complexity, not operation complexity. How much memory does Arrays.sort() use (I don't know TBH)? – Argote Feb 04 '11 at 22:59
  • Good point. I completely ignored the memory complexity here. The point was to show that the operational complexity will be higher, as will be code complexity. But since memory complexity was mentioned in the question, that should have been addressed in my answer. From what I read in the docs, Arrays.sort() internally uses quicksort or mergesort, so I'd guess memory consumption O(log(n)) or O(n). – Axel Feb 05 '11 at 10:14
0

I assume you have no restriction on time complexity of your algorithm. My suggestion is to append the values of a1 to a2 and apply any O(nlogn) sorting algorithm like quicksort. However if you'd like to do the merging, i think this code helps you:

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;
    }
Abbas
  • 3,228
  • 1
  • 23
  • 27
  • Arrays start sorted per question. –  Feb 04 '11 at 23:33
  • My test is the same as it is described in the original question, both subarrays are sorted (-1,0,7,8) and (1,3,9,0,0,0,0). The trailing zeros are there for the final result. That's why my function gets the size of the second array (3). – Abbas Apr 01 '11 at 00:24
0

/or simply you can do this->/

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;

 }
0

A quick approach is by using a Heap. Check this out:

public static void merge(long arr1[], long arr2[], int n, int m) 
    {
        PriorityQueue<Long> pq=new PriorityQueue<>();
        for(int i=0; i<n; i++){
            pq.add(arr1[i]);
        }
        for(int j=0; j<m; j++){
            pq.add(arr2[j]);
        }
        for(int k=0; k<n; k++){
            arr1[k]=pq.poll();
        }
        for(int l=0; l<m; l++){
            arr2[l]=pq.poll();
        }
    }
-1

have you tried System.arrayCopy? Something like :

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

Should do the trick while not wasting time (arraycopy is optimized for these use cases).

Kellindil
  • 4,523
  • 21
  • 20
  • How would that result in a sorted array? – Argote Feb 04 '11 at 22:46
  • The OP isn't trying to *copy* the arrays as much as *merge* them. This requires the two ranges to be interleaved in sorted order. – templatetypedef Feb 04 '11 at 22:46
  • ha, my bad... read 'elements of a1 and a2 in that order' ... instead of 'in sorted order'. He could still use arraycopy and Arrays.sort() afterwards though. – Kellindil Feb 04 '11 at 22:50