什么是合并排序的更好方法?递归函数还是非递归函数?

时间:2019-04-19 07:51:04

标签: c sorting mergesort

我正在搜索合并排序,发现了两种功能。

第一个使用这样的递归。

#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()*/

我认为这比使用递归更好。此函数将递归减少到一系列forwhile循环!当然,他们的行为有所不同。我认为递归函数对编译器不利。我说的对吗?

3 个答案:

答案 0 :(得分:2)

假设优化了实现,自下而上的迭代合并排序比自上而下的递归合并排序要快一些,因为它跳过了索引的递归生成。对于较大的阵列,与O(n log(n))的总时间相比,自上而下的额外开销相对较小,O(log(n)),在两种情况下,大部分时间都花在了合并上自下而上和自上而下都相同。自上而下的合并排序使用O(log(n))堆栈空间,而两者都使用O(n)工作空间。但是,几乎所有稳定排序的库实现都是迭代的自下而上合并排序的变体,例如插入排序和自下而上合并排序的混合。

链接到显示优化的自上而下合并排序的答案,使用一对相互递归的函数控制合并的方向以避免数据复制:

Mergesort implementation is slow

链接到答案,不包括快速排序,2种自下而上的合并排序和4种自下而上的合并排序:

Optimized merge sort faster than quicksort

答案 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)