2

given an integer array of which first and second half are sorted. write a function to merge the two parts to create one single sorted array in place(do not use extra space). one approach is eg: // 1,3,6,8,-5,-2,3,8

int l = 0;
int u = size;
int mid = (l+u)/2;
int i;
for (i = 0; i < mid; i++) {
  for (j = mid; j < size; j++) {
    if (a[i] >= a[j]) {
      temp = a[j];
      for (i = mid-1; i >= 0; i--)
        a[i+1] = a[i];
      a[0] = temp;
    }
  }
}

but i think there must be some O(n) algo for this..

Edwin Buck
  • 69,361
  • 7
  • 100
  • 138
dreamer
  • 478
  • 1
  • 11
  • 24

5 Answers5

3

Indeed there are O(n) algorithms for merging in-place. They're usually quite complex though. For few ideas, please see the papers such as Huang, Langston (PDF) or Katajainen, Pasanen, Teuhola (PostScript). These are actually defined based on precise terms of what kind of extra memory is allowed, without cheating by using recursion stack and so on.

liori
  • 40,917
  • 13
  • 78
  • 105
2

You can do this by walking the array once, from 0 to it's last element. You just need to think about what you need to compare or swap the "current" element against, and it becomes quite easy.

You'll have to keep a few extra variables around to track the current element, and the "other" element you need to compare it to.

Also, you might want to look into the major C code formatting styles. There is no style that makes everyone 100% happy, but there are plenty of styles that make everyone unhappy.

---- Edited ----

Ok, this is much more like a "brain teaser" problem than a serious computer science problem. The issue is that "extra memory" is very poorly defined, and so there's a tons of way you can achieve the outcome if you only remember that recursion is allowed in the programming language, and that a recursive call requires extra stack (which nobody is going to consider memory allocation as it required by the programming language implementation).

Basically, such a question is meant to see if you look at a problem and see a recursive solution.

#include <stdio.h>

void sort(int index, int start1, int end1, int start2, int end2, int* array) {
  if (index >= end2) {
    return;
  }
  int lower;
  if (array[start1] <= array[start2]) {
    lower = array[start1];
    sort(index+1, start1+1, end1, start2, end2, array);
  } else {
    lower = array[start2];
    sort(index+1, start1, end1, start2+1, end2, array);
  }
  array[index]=lower;
}

int main(int argc, char** argv) {
  int a[] = {1,3,6,8,-5,-2,3,8};
  sort(0, 0, 3, 4, 7, a);
  int i;
  for (i = 0; i <= 7; i++) {
    printf("%d, ", a[i]);
  }
  printf("\n");
  return 0;
}

Is it a cheat? You decide. However, the sort was done in-place, and the cached numbers were neatly tucked away in local space in the call the stack, where you cannot really allocate / de-allocate them.

Extra optimizations are possible, but they are improvements of the core idea: use recursion and the call stack to cache information.

Edwin Buck
  • 69,361
  • 7
  • 100
  • 138
  • 1
    @Edwin Buck are you sure you can do it in one pass with no auxiliary memory? – sverre May 27 '11 at 15:03
  • 2
    @sverre, yes you can. You do need memory to hold the indexes, and it would be a little easier to have memory to act as a the holder for the element being swapped, but even if you don't have memory to act as a holder for the element being swapped, you can do it by the old XOR swap trick. – Edwin Buck May 27 '11 at 15:40
  • @sverre, the reason it is possible is because it's not a real sort, it's just a merge. Imagine if it were two decks of already sorted cards, the answer would be to just pull the card with the lower value, one comparison and one pull per card is O(n). – Edwin Buck May 27 '11 at 15:43
  • @Edwin Buck I know it's not a sort, but I still don't get it, because after you swap the other part-array is no longer sorted. In light of http://stackoverflow.com/questions/2126219/how-to-merge-two-sorted-integer-array-in-place-using-on-time-and-o1-space-cos I'm not the only one. Could you post some code? – sverre May 27 '11 at 15:44
  • I can easily merge two decks of cards in-place, because they are linked lists and not arrays. I just don't see how to generalize it to arrays. – sverre May 27 '11 at 15:47
  • @sverre, edited with a working code solution. Like most questions of this sort, it's a trick question, which hinges on the exact definition of extra memory. In this case, it requires no extra heap. – Edwin Buck May 27 '11 at 19:10
  • 2
    @Edwin: This is actually a serious computer science problem, see [previous](http://stackoverflow.com/questions/3285756/) [instances](http://stackoverflow.com/questions/2126219) of this question. There's a precise definition of extra memory: it's all the memory you use other than to store original data. Here you're clearly using Θ(recursion depth) memory, and your recursion depth is Θ(array size) — in effect you are doing the merge with a copy of half the array, when the whole point of the question was do cope without this extra memory. – Gilles 'SO- stop being evil' May 27 '11 at 20:04
  • 1
    @Gilles: Indeed, this answer is not a solution to the problem. Hand-waving the memory usage of recursion away is an incorrect and extremely dangerous programming practice. – R.. GitHub STOP HELPING ICE May 27 '11 at 20:27
  • 1
    If you are doing your serious computer science research by asking stack overflow, bigger problems exist. – Edwin Buck May 27 '11 at 20:46
1

The best answer I've found, assuming you can't use any auxiliary memory, is to heapsort the array, which is theta(n log n), somewhat faster than your current insertion sort scheme.

sverre
  • 6,768
  • 2
  • 27
  • 35
0

any of following

void Merge(int array[N])
{
for(int i=N/2;i<N;i++){
    int j=i;
    while(array[j]<array[j-1]&& j>=1){
        int temp=array[j];
        array[j]=array[j-1];
        array[j-1]=temp;
        j--;
    }
}
}

void Merge2(int array[N])
{
for(int i=N/2;i<N;i++){
   int key=array[i];
   int j=i-1;
   while(array[j]>key&& j>=0){
           array[j+1]=array[j];
           j--;
   }
   array[j+1]=key;
}
}
Avinash
  • 173
  • 1
  • 4
  • 17
0

I was looking at this again and here is your O(n-1) and memory(n+1):

/**
 * Created by deian on 2016-12-22.
 */
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) {
        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;
            // i1 and i2 - always point to the smallest known numbers
            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};
        merge(a);
    }
}
Deian
  • 1,237
  • 15
  • 31
  • I tried running your code (after changing `ri == rs` to `ri = rs`) and it does not seem to work. – sverre May 27 '11 at 16:17
  • after the else if it should be `li++; ri = rs;` but this is not O(n) if you reset one of the index -- that goes to O(n log n) – Hogan May 27 '11 at 17:02
  • @Hogan that simply can't be correct, since if you have `li++` on both branches of the `if` you will certainly finish in `O(n)`, just not always with the correct answer. – sverre May 27 '11 at 18:10
  • @sverre: You might be right, since it is clear this solution a second inner loop. maybe something like if (a[li] =< a[ri]) li++; but after the ri = rs assignment. – Hogan May 27 '11 at 18:23
  • @sverre and @hogan you are right. look at the latest - the idea is the same though - we need to keep the arrays sorted while we doing the swaps – Deian May 27 '11 at 22:06
  • @deian better, but what is the complexity (hint: it's not `O(n)`)? – sverre May 27 '11 at 22:11