所以我想知道Java中合并排序的最有效实现是什么(如果它的时间效率可以根据语言而改变)。这个问题可能微不足道,但我的最终目标是向更有经验的程序员学习。以下是我做的两个例子:
//version I made.
public static double[] mergeSort(double[] arreglo) {
if (arreglo.length > 1) {
int d = (arreglo.length / 2);
double[] arreglo1 = Arrays.copyOfRange(arreglo, 0, d),
arreglo2 = Arrays.copyOfRange(arreglo, d, arreglo.length);
arreglo1 = mergeSort(arreglo1);
arreglo2 = mergeSort(arreglo2);
return merge(arreglo1, arreglo2);
} else {
return arreglo;
}
}
public static double[] merge(double[] arreglo1, double[] arreglo2) {
double[] convi = new double[arreglo1.length + arreglo2.length];
for (int i = 0, m1 = 0, m2 = 0; i < convi.length; i++) {
if (arreglo1.length > m1 && arreglo2.length > m2) {
if (arreglo1[m1] <= arreglo2[m2])
convi[i] = arreglo1[m1++];
else {
convi[i] = arreglo2[m2++];
}
} else {
convi[i] = (arreglo1.length == m1) ? arreglo2[m2++] : arreglo1[m1++];
}
}
return convi;
}
//Taken out of Cormens book.
public static void mergeSort(int[] arreglo, int i, int f) {
if (f > i) {
int d = ((i + f) / 2);
mergeSort(arreglo, i, d);
mergeSort(arreglo, d + 1, f);
merge(arreglo, i, d, f);
}
}
public static void merge(int[] arreglo, int i, int m, int f) {
int n1 = (m - i) + 1;
int n2 = (f - m);
int[] mitad1 = new int[n1 + 1];
int[] mitad2 = new int[n2 + 1];
for (int v = 0; v < n1; v++) {
mitad1[v] = arreglo[i + v];
}
for (int p = 0; p < n2; p++) {
mitad2[p] = arreglo[p + m + 1];
}
mitad1[n1] = Integer.MAX_VALUE;
mitad2[n2] = Integer.MAX_VALUE;
for (int r = i, m1 = 0, m2 = 0; r <= f; r++) {
if (mitad1[m1] <= mitad2[m2]) {
arreglo[r] = mitad1[m1];
m1++;
} else {
arreglo[r] = mitad2[m2];
m2++;
}
}
}
答案 0 :(得分:2)
第二个效率要高得多,因为它避免了所有的复制步骤。它也是一百万倍的简单和明显,它有自己的价值,
答案 1 :(得分:1)
以下程序是从Robert Sedgewick's Algorithms in C++, Parts 1-4
中给出的C ++示例翻译而来的它引入了一种改进。它将整个排序数组的单个副本放入一个进一步处理的辅助数组中。接下来,通过在辅助阵列和原始阵列之间交替,在辅助阵列上完成递归分割,从而不会发生合并阵列的额外复制操作。基本上,算法在每次递归调用中切换输入和辅助数组的角色。例如,概念上:
常规合并:
- 合并
(((8) (5))((2) (3)))(((1) (7))((4) (6)))
(( 5 8 )( 2 3 ))(( 1 7 )( 4 6 ))
-- copy back and ignore previous (UNNECESSARY)
(( 5 8 )( 2 3 ))(( 1 7 )( 4 6 ))
- - - - - - - -
这个程序:
- 合并
(((8) (5))((2) (3)))(((1) (7))((4) (6)))
(( 5 8 )( 2 3 ))(( 1 7 )( 4 6 ))
- 向后合并
( 2 3 5 8 )( 1 4 6 7 )
(( 5 8 )( 2 3 ))(( 1 7 )( 4 6 ))
此外,在将数组拆分为两半后,该算法会使用insertion sort
,因为它在小数据集上的表现优于merge sort
。准确使用insertion sort
的时间阈值可以通过反复试验确定。
代码:
static int M = 10;
//insertion sort to be used once the mergesort partitions become small enough
static void insertionsort(int[] a, int l, int r) {
int i, j, temp;
for (i = 1; i < r + 1; i++) {
temp = a[i];
j = i;
while ((j > 0) && a[j - 1] > temp)
{
a[j] = a[j - 1];
j = j - 1;
}
a[j] = temp;
}
}
//standard merging two sorted half arrays into single sorted array
static void merge(int[] merged_a, int start_a, int[] half_a1, int start_a1, int size_a1,
int[] half_a2, int start_a2, int size_a2) {
int i, j, k;
int total_s = size_a1 + size_a2;
for (i = start_a1, j = start_a2, k = start_a; k < (total_s); k++) {
// if reached end of first half array, run through the loop
// filling in only from the second half array
if (i == size_a1) {
merged_a[k] = half_a2[j++];
continue;
}
// if reached end of second half array, run through the loop
// filling in only from the first half array
if (j == size_a2) {
merged_a[k] = half_a1[i++];
continue;
}
// merged array is filled with the smaller element of the two
// arrays, in order
merged_a[k] = half_a1[i] < half_a2[j] ?
half_a1[i++] : half_a2[j++];
}
}
//merge sort data during merging without the additional copying back to array
//all data movement is done during the course of the merges
static void mergesortNoCopy(int[] a, int[] b, int l, int r) {
if (r - l <= M) {
insertionsort(a + l, l, r - l);
return;
}
int m = (l + r) / 2;
//switch arrays to msort b thus recursively writing results to b
mergesortNoCopy(b, a, l, m); //merge sort left
mergesortNoCopy(b, a, m + 1, r); //merge sort right
//merge partitions of b into a
merge(a, l, b, l, m - l + 1, b, m + 1, r - m); //merge
}
static void mergesort(int[] a) {
int[] aux = Arrays.copyOf(a, a.length);
mergesortNoCopy(a, aux, 0, a.length - 1);
}
其他一些可能的改进:
如果已经排序则停止。
检查上半场中最大项目是否是下半场最小项目。 帮助部分排序的数组。
// after split, before merge
if (a[mid] <= a[mid + 1]) return;
编辑:这是我在不同版本的Mergesort上发现的good document及其改进。