我需要使用额外的数组进行合并排序。这是我的代码:
public class extra_storage{
public static void main(String[]args) {
int x[]=new int[]{12,9,4,99,120,1,3,10};
int a[]=new int[x.length];
mergesort(x,0,x.length-1,a);
for (int i=0;i<x.length;i++){
System.out.println(x[i]);
}
}
public static void mergesort(int x[],int low,int high, int a[]){
if (low>=high) {
return;
}
int middle=(low+high)/2;
mergesort(x,low,middle,a);
mergesort(x,middle+1,high,a);
int k;
int lo=low;
int h=high;
for (k=low;k<high;k++)
if ((lo<=middle ) && ((h>high)||(x[lo]<x[h]))){
a[k]=x[lo++];
}
else {
a[k]=x[h++];
}
for (k=low;k<=high;k++){
x[k]=a[k];
}
}
}
但是出了点问题。当我运行它时输出是这样的:
1 0 3 0 4 0 9 0
有什么问题?
答案 0 :(得分:2)
这是您的原始算法,其中包含一些更正和风格改进:
public class MergeSort {
public static void main(String[]args) {
int[] nums = {12,9,4,99,120,1,3,10};
mergeSort(nums);
System.out.println(java.util.Arrays.toString(nums));
// "[1, 3, 4, 9, 10, 12, 99, 120]"
}
static void mergeSort(int[] arr) {
mergeSort(arr, 0, arr.length - 1, new int[arr.length]);
}
static void mergeSort(int[] arr, int low, int high, int[] buff){
if (low >= high) {
return;
}
int mid = (low + high) >>> 1;
mergeSort(arr, low, mid, buff);
mergeSort(arr, mid+1, high, buff);
for (int left = low, right = mid + 1, i = low; i <= high; i++) {
if (right > high || left <= mid && arr[left] <= arr[right]) {
buff[i] = arr[left++];
} else {
buff[i] = arr[right++];
}
}
for (int i = low; i <= high; i++) {
arr[i] = buff[i];
}
}
}
与Eyal的实现不同,src
和dst
的角色在递归级别之间来回交换,这里我们总是排序到相同的数组对象arr
,并且数组对象buff
始终仅用作合并的临时缓冲区(因此,在合并阶段之后存在复制阶段)。这仍然是O(N log N)
,但Eyal更高级的实施将是一个不变因素的改进。
基本上,左子阵列有left
索引,右子阵列有right
索引,你可以从left
或right
中选择正确的元素放入buff
。
有效的元素范围是(包含边界):
left = low...mid
right = mid+1...high
用于右子阵列要评估要选择的元素,请考虑选择left
元素的条件。它发生在:
right > high
)left <= mid
)和(有条件地)该元素小于或等于来自右子阵列的元素(即arr[left] <= arr[right]
)。在此处使用短路条件和&&
(JLS 15.23)和条件 - 或||
(JLS 15.24)运算符非常重要,并相应地对这些条件进行排序。否则你会得到ArrayIndexOutOfBoundsException
。
通常会看到以下内容:
int mid = (low + high) / 2; // BROKEN! Can result in negative!
问题是,现在,数组/列表等很容易超过2个 30 元素,上面会导致溢出并导致负数。
Josh Bloch倡导的新成语如下:
int mid = (low + high) >>> 1; // WORKS PERFECTLY!
这使用无符号右移运算符(JLS 15.19);它根据我们的需要正确处理添加的任何溢出。
不要养成这样声明数组的习惯:
int x[];
您应该使用类型而不是标识符放置括号:
int[] x;
答案 1 :(得分:1)
您似乎有堆栈溢出。
在您的代码中
public static void mergesort(int x[],int low,int high, int a[]){
if (low>high) {
return;
}
int middle=(low+high)/2;
mergesort(x,low,middle,a);
mergesort(x,middle+1,high,a);
如果低开始低或高等于高,那么它将最终等于高,在这种情况下,中间==低==高,它将自动调用自身。
答案 2 :(得分:1)
您的代码不够清晰,并且有许多不相关的操作。此外,它没有表现出您描述的行为。
您尝试实现的mergesort版本的想法是使用与输入数组相同大小的单个辅助数组(源数组)(目标数组 )。这允许从一个阵列合并到另一个阵列,因为没有有效的就地合并技术。该算法包括将目标数组的两半分类到源数组中的相应范围,然后将两半合并回目标数组。请注意,这要求在每次调用时,两个数组在由low和high指定的范围内是相同的。
以下是int数组的实现。您可以添加优化,例如对小输入执行插入排序,或者在可能的情况下附加一半而不是合并它们。这种优化可以在Arrays.sort(Object[])的实现中找到。
public static void mergeSort(int[] arr){
int[] aux = arr.clone();
mergeSort(aux, 0, arr.length, arr);
}
private static void mergeSort(int[] src, int low, int high, int[] dst) {
// One or no items - nothing to sort
if (high-low<=1)
return;
// Recursively sorting into src
int mid = (low + high) / 2;
mergeSort(dst, low, mid, src);
mergeSort(dst, mid, high, src);
// Merge halves into dst
for(int i = low, p = low, q = mid; i < high; i++) {
if (q >= high || p < mid && src[p] <= src[q])
dst[i] = src[p++];
else
dst[i] = src[q++];
}
}