合并两个排序的半部分没有额外的内存!

时间:2011-05-27 14:48:08

标签: c sorting merge

给出一个整数数组,其中第一和第二半被排序。编写一个函数来合并这两个部分来创建一个单独的排序数组(不要使用额外的空间)。 一种方法是 例如:     // 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;
    }
  }
}

但我认为必须有一些O(n)算法......

5 个答案:

答案 0 :(得分:3)

确实存在用于就地合并的O(n)算法。但它们通常很复杂。对于一些想法,请参阅Huang, Langston (PDF)Katajainen, Pasanen, Teuhola (PostScript)等论文。这些实际上是基于允许哪种额外内存的精确术语来定义的,而不会通过使用递归堆栈等来作弊。

答案 1 :(得分:2)

你可以通过走一次数组,从0到它的最后一个元素来做到这一点。你只需要考虑比较或交换“当前”元素所需的内容,它就变得非常容易了。

你必须保留一些额外的变量来跟踪当前元素,以及你需要比较它的“其他”元素。

此外,您可能希望查看主要的C代码格式样式。没有一种风格让每个人都百分百满意,但有很多风格让每个人都不高兴。

----编辑----

好吧,这更像是一个“脑筋急转弯”问题而非严重的计算机科学问题。问题在于“额外内存”的定义非常糟糕,因此如果您只记得编程语言中允许递归,那么有很多方法可以实现结果,that a recursive call requires extra stack(没有人会去考虑编程语言实现所需的内存分配。

基本上,这样的问题是为了看看你是否看到问题并看到递归解决方案。

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

这是骗子吗?你决定。但是,排序是就地完成的,缓存的数字整齐地隐藏在调用堆栈的本地空间中,在那里你无法真正分配/解除分配它们。

可以进行额外的优化,但它们是核心思想的改进:使用递归和调用堆栈来缓存信息。

答案 2 :(得分:1)

我发现,假设您不能使用任何辅助内存,我发现的最佳答案是对数组进行导航,这是theta(n log n),比您当前的插入排序方案稍快一些。

答案 3 :(得分:0)

我再次看到这个,这是你的O(n-1)和记忆(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);
    }
}

答案 4 :(得分:0)

以下任何一项

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