问题描述
|
给定一个整数数组,其前半部分和后半部分已排序。编写一个函数将两个部分合并以在原位置创建一个单个排序的数组(不要使用多余的空间)。
一种方法是
例如:
// 1,3,6,8,-5,-2,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)算法。
解决方法
我又在看这个,这是你的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,%d)s(%d>%d)i(%d,%d) \\n\",di+1,oi1,oi2,ni,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,8};
// int[] a = new int[]{1,2,8};
int[] a = new int[]{1,5,4};
merge(a);
}
}
, 实际上,有O(n)个算法可以就地合并。它们通常很复杂。对于一些想法,请参见Huang,Langston(PDF)或Katajainen,Pasanen,Teuhola(PostScript)之类的论文。这些实际上是根据允许什么样的额外内存的精确术语定义的,而不会通过使用递归堆栈等来作弊。
, 您可以通过将数组从0遍历到最后一个元素一次来完成此操作。您只需要考虑比较或交换\“ current \”元素所需要的内容,这变得非常容易。
您将不得不保留一些额外的变量来跟踪当前元素以及需要与之进行比较的\“ other \”元素。
另外,您可能想研究主要的C代码格式样式。没有什么风格可以使每个人都100%满意,但是有很多风格可以使每个人都不满意。
----编辑----
好的,这更像是一个“脑筋急转弯”问题,而不是一个严重的计算机科学问题。问题是“额外的内存”定义很差,因此,如果您只记得编程语言中允许进行递归,并且递归调用需要额外的堆栈,则有很多方法可以实现结果(没有人会考虑编程语言实现所需的内存分配)。
基本上,这样的问题是要看您是否正在看问题并看到递归解决方案。
#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,start2+1,array);
}
array[index]=lower;
}
int main(int argc,char** argv) {
int a[] = {1,8};
sort(0,4,7,a);
int i;
for (i = 0; i <= 7; i++) {
printf(\"%d,\",a[i]);
}
printf(\"\\n\");
return 0;
}
是作弊吗?你决定。但是,排序是就地完成的,并且缓存的数字被整齐地藏在调用堆栈的本地空间中,在这里您无法真正分配/取消分配它们。
可以进行额外的优化,但这是对核心思想的改进:使用递归和调用堆栈来缓存信息。
, 假设您不能使用任何辅助内存,我发现的最佳答案是对数组进行堆排序,即3%,比您当前的插入排序方案要快一些。
, 以下任何一项
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;
}
}