我正在搜索合并排序,发现了两种功能。
第一个使用这样的递归。
#include <stdio.h>
void merge(array, low, mid, high) {
int temp[MAX];
int i = low;
int j = mid + 1;
int k = low;
while ((i <= mid) && (j <= high)) {
if (array[i] <= array[j])
temp[k++] = array[i++];
else
temp[k++] = array[j++];
}/*End of while*/
while (i <= mid)
temp[k++] = array[i++];
while (j <= high)
temp[k++] = array[j++];
for (i = low; i <= high; i++)
array[i] = temp[i];
}/*End of merge()*/
void merge_sort(int low, int high) {
int mid;
if (low != high) {
mid = (low + high) / 2;
merge_sort(low, mid);
merge_sort(mid + 1, high);
merge(low, mid, high);
}
}/*End of merge_sort*/
然后,我认为递归函数不适用于大型数组。在这种情况下,此函数会导致很多递归调用。我认为这是不好的编程方式。 (实际上,我不喜欢递归。)
所以,我找到了另一种方式,没有递归的合并排序功能:
#include <stdio.h>
#define MAX 30
int main() {
int arr[MAX], temp[MAX], i, j, k, n, size, l1, h1, l2, h2;
printf("Enter the number of elements : ");
scanf("%d", &n);
for (i = 0; i < n; i++) {
printf("Enter element %d : ", i + 1);
scanf("%d", &arr[i]);
}
printf("Unsorted list is : ");
for (i = 0; i < n; i++)
printf("%d ", arr[i]);
/* l1 lower bound of first pair and so on */
for (size = 1; size < n; size = size * 2) {
l1 = 0;
k = 0; /* Index for temp array */
while (l1 + size < n) {
h1 = l1 + size - 1;
l2 = h1 + 1;
h2 = l2 + size - 1;
/* h2 exceeds the limlt of arr */
if (h2 >= n)
h2 = n - 1;
/* Merge the two pairs with lower limits l1 and l2 */
i = l1;
j = l2;
while (i <= h1 && j <= h2) {
if (arr[i] <= arr[j])
temp[k++] = arr[i++];
else
temp[k++] = arr[j++];
}
while (i <= h1)
temp[k++] = arr[i++];
while (j <= h2)
temp[k++] = arr[j++];
/** Merging completed **/
/*Take the next two pairs for merging */
l1 = h2 + 1;
}/*End of while*/
/*any pair left */
for (i = l1; k < n; i++)
temp[k++] = arr[i];
for (i = 0; i < n; i++)
arr[i] = temp[i];
printf("\nSize=%d \nElements are : ", size);
for (i = 0; i < n; i++)
printf("%d ", arr[i]);
}/*End of for loop */
printf("Sorted list is :\n");
for (i = 0; i < n; i++)
printf("%d ", arr[i]);
printf("\n");
return 0;
}/*End of main()*/
我认为这比使用递归更好。此函数将递归减少到一系列for
和while
循环!当然,他们的行为有所不同。我认为递归函数对编译器不利。我说的对吗?
答案 0 :(得分:2)
链接到显示优化的自上而下合并排序的答案,使用一对相互递归的函数控制合并的方向以避免数据复制:
Mergesort implementation is slow
链接到答案,不包括快速排序,2种自下而上的合并排序和4种自下而上的合并排序:
答案 1 :(得分:1)
您说得对。自上而下的迭代合并排序比自上而下的递归合并排序更快。两种方法都适合编译器;),但是递归方法需要花费更多的时间来编译。
答案 2 :(得分:1)
您用于合并排序的递归方法的代码存在问题:
merge
的原型没有参数类型。merge_sort
的参数列表中缺少数组high
作为最后一个元素的索引容易出错,并且不允许使用空数组。您应该将索引传递到数组末尾之外的第一个元素,这样high - low
是切片中要排序的元素数。这样,第一次调用merge_sort
可以占用0
和数组大小。int temp[MAX];
既浪费又不正确。浪费,因为大小可能远远超过所需大小,从而导致潜在的堆栈溢出;如果high - low + 1
大于MAX
,则大小不正确,从而导致超出数组末尾的写入,从而导致未定义的行为。此merge_sort
函数将最多递归调用 log 2 (高-低)次,每次调用都分配一个临时的本地数组。递归调用的数量不是问题,对于10亿条记录而言,只有30个,但是本地数组才是!如果尝试对大型数组进行排序,则堆栈上可能没有足够的空间来存储该数组的副本,而要复制的副本就少了很多,从而导致行为未定义,很可能导致崩溃。
但是请注意,您发现的迭代方法存在同样的问题,因为它也为temp[MAX]
分配了自动存储空间。
解决方案是从顶部的堆中分配一个临时数组,并将其传递给递归函数。
这是改进版:
#include <stdio.h>
static void merge(int *array, int *temp, int low, int mid, int high) {
int i = low;
int j = mid;
int k = 0;
while (i < mid && j < high) {
if (array[i] <= array[j])
temp[k++] = array[i++];
else
temp[k++] = array[j++];
}
while (i < mid)
temp[k++] = array[i++];
while (j < high)
temp[k++] = array[j++];
for (i = low, k = 0; i < high; i++, k++)
array[i] = temp[k];
}
static void merge_sort_aux(int *array, int *temp, int low, int high) {
if (high - low > 1) {
int mid = (low + high) / 2;
merge_sort_aux(array, temp, low, mid);
merge_sort_aux(array, temp, mid, high);
merge(array, temp, low, mid, high);
}
}
int merge_sort(int *array, int size) {
if (size > 1) {
int *temp = malloc(size * sizeof(*temp));
if (temp == NULL)
return -1;
merge_sort_aux(array, temp, 0, size);
free(temp);
}
return 0;
}
// call from main as merge_sort(arr, MAX)