最近,我通过简单地在小子列表中使用 InsertionSort 来着迷于 ShellSort 算法的想法,然后最终使用InsertionSort作为整个列表。
所以,我想为什么不将 MergeSort 与 InsertionSort 结合使用(而不是使用Merge()函数,让我们使用 InsertionSort )。由于InsertionSort擅长排序部分排序列表,而 MergeSort 的想法是将两个排序列表合并为一个排序列表。
然后,我使用merge()函数测试MergeSort,使用带有随机值10,000,000个元素的ArrayionSort()测试MergeSort。事实证明,带有InsertionSort()的MergeSort比带有merge()函数的MergeSort执行快几倍。由于提出准确的数学证明超出了我的能力,我来这里是为了寻找合乎逻辑的理由。以下是我要确认的内容:
以下是MergeSort()
的实现public static void MergeSort(int[] array)
{
int[] aux = new int[array.Length];
MergeSort(array, aux, 0, array.Length - 1);
}
public static void MergeSort(int[] array, int[] aux, int low, int high)
{
if (low >= high) return;
int mid = (low + high) / 2;
MergeSort(array, aux, low, mid);
MergeSort(array, aux, mid + 1, high);
Merge(array, aux, low, mid, high);
}
protected static void Merge(int[] array, int[] aux, int low, int mid, int high) {
// copy into aux array
for (int i = low; i <= high; i++) aux[i] = array[i];
// merge
int j = low, k = mid + 1;
for (int o = low; o <= high; o++) {
if (j > mid)
array[o] = aux[k++];
else if (k > high)
array[o] = aux[j++];
else if (aux[k] < aux[j])
array[o] = aux[k++];
else
array[o] = aux[j++];
}
}
以下是带有InsertionSort()
的MergeSortpublic static void MergeInsertionSort(int[] array)
{
MergeInsertionSort(array, 0, array.Length - 1);
}
public static void MergeInsertionSort(int[] array, int low, int high)
{
if (low >= high) return;
if (low + 1 == high) {
// sort two elements
if (array[low] > array[high]) {
int tmp = array[low];
array[low] = array[high];
array[high] = tmp;
}
} else {
int mid = (low + high) / 2;
MergeInsertionSort(array, low, mid);
MergeInsertionSort(array, mid + 1, high);
// do insertion sort
for (int i = mid + 1, j; i <= high; i++) {
int ins = array[low];
// move the element into correct position
for (j = i - 1; (j >= low) && (ins < array[j]); j--) {
array[j + 1] = array[j];
}
array[j + 1] = ins;
}
}
}
以下是可运行的代码,您可以在计算机上测试它: http://pastebin.com/4nh7L3H9
答案 0 :(得分:1)
你根本没有测试同样的东西。您的Merge
方法使用辅助数组,它首先要做的是在执行实际的合并工作之前将初始数组复制到aux数组。因此,每次调用Merge
时,您最终会完成两倍的工作。
您可以通过array
和aux
进行智能交换来消除额外的副本。这在非递归实现中更容易处理,但是递归版本可以实现。我将此作为练习。
您的MergeInsertionSort
方法的运作方式大不相同。它根本没有进行合并。它只是拆分数组并在越来越大的范围内进行插入排序。
我们的想法是使用插入排序,以便在范围较小时减少合并的开销。通常它看起来像这样:
public static void MergeSort(int[] array, int[] aux, int low, int high)
{
if (low >= high) return;
if ((high - low) < MergeThreshold)
{
// do insertion sort of the range here
}
else
{
int mid = (low + high) / 2;
MergeSort(array, aux, low, mid);
MergeSort(array, aux, mid + 1, high);
Merge(array, aux, low, mid, high);
}
}
然后将MergeThreshold
设置为&#34;小范围&#34;您确定的价值是合适的。通常情况下,这个范围在5到20之间,但您可能希望尝试不同的值和不同的类型(整数,字符串,复杂对象等)以获得良好的全面数字。
答案 1 :(得分:0)
这里的问题是您的插入排序已损坏,使其运行速度快得多,但返回了无意义的输出(所有元素在外观上均具有相同的编号)。这是运行tmp
之后的MergeInsertionSort
数组的示例(数组大小更改为10):
1219739925
1219739925
1219739925
1219739925
1219739925
1219739925
1219739925
1219739925
1219739925
1275593566
这是您的代码:
// do insertion sort
for (int i = mid + 1, j; i <= high; i++) {
int ins = array[low];
// move the element into correct position
for (j = i - 1; (j >= low) && (ins < array[j]); j--) {
array[j + 1] = array[j];
}
array[j + 1] = ins;
}
问题是这条线
int ins = array[low];
这应该是:
int ins = array[i];
解决此问题后,您会发现MergeSort
比MergeInsertionSort
更有效率(您必须减小数组大小,以便在合理的时间内运行)。
另一方面,这花了我一段时间,因为我最初只是检查输出是否已排序(是,但不是输入的排序版本),而不是实际检查它是否是输入的排序版本。直到我查看发现问题的输出。