0

我正在尝试在 C 中实现合并排序算法。我了解该算法应该如何工作,但是我在实现时遇到了一些困难。

我知道它的实现有数百个示例和源代码,但我希望有人能帮助我理解为什么我的工作不正常。

我的代码在下面,在代码之后我解释了我到目前为止所做的尝试。

#include <stdio.h>

void merge(int a[], int L[], int R[],int nL, int nR) //nL and nR are the lengths of L[] and R[]
{
   int i = 0 , j = 0, k = 0;

    while(i<nL && j <nR)
    {
        if(L[i] <= R[j]){
            a[k] = L[i];
            i++;
        }
        else{
            a[k] = R[j];
            j++;
        }

        k++;
     }

     while(i < nL){
        a[k] = L[i];
        i++;
        k++;
     }

     while(j < nR) {
        a[k] = R[j];
        j++;
        k++;
     }
}   

void mergesort(int a[],int n)    //n is the length of a[]
{
   if(n < 2) return;     //BASE CASE
   int mid = n / 2;

   int left[mid];
   int right[n-mid];

   for(int i = 0; i < mid; i++)
   {
        left[i] = a[i];
   }

   for(int i = mid; i < n-1; i++)
   {
        right[i-mid] = a[i];
   }

   int nL = sizeof(left) / sizeof(left[0]);
   int nR = sizeof(right) / sizeof(right[0]);

   mergesort(left, nL);
   mergesort(right, nR);
   merge(a,left,right,nL,nR); 
}    


int main(void)
{
    printf("Initial:\n");
    printf("3 4 1 6\n");

    int numbers[4] = {3,4,1,6};
    int n = sizeof(numbers) / sizeof(int);

    mergesort(numbers,n);
    printf("Sorted:\n");
    for(int i =0 ; i < 4; i++)
    {
        printf("%d ", numbers[i]);
    }

    return 0;
}  

照原样使用未排序的数组[3,4,1,6],输出为0 0 1 3. 显然 1 和 3 相对于彼此的顺序是正确的,但开头的两个零显然是错误的。起初,在我看来,我将 4 和 6 插入到数组的右侧并且超出了数组的范围。

我使用了一些打印语句来尝试调试,但我无法弄清楚发生了什么。我什至尝试使用 gdb 跟踪我的代码,但我仍然无法对其进行排序。

有没有人对可能发生的事情有任何想法?

4

3 回答 3

3

编写代码的一种更接近惯用的方式merge()是:

void merge(int a[], int L[], int R[],int nL, int nR)
{
    int i = 0, j = 0, k = 0;

    while (i < nL && j < nR)
    {
        if (L[i] <= R[j])
            a[k++] = L[i++];
        else
            a[k++] = R[j++];
    }
    while (i < nL)
        a[k++] = L[i++];  
    while (j < nR)
        a[k++] = R[j++];
}

这大约是代码行数的一半,并且在广泛的范围内,要阅读的代码越少越好。有些人坚持在每个循环或条件之后使用大括号。我不认为这是必要的(或特别有用),但如果这是你喜欢的风格,你可以使用它。

您的mergesort()代码不那么松弛,但可以更改为:

void mergesort(int a[],int n)    //n is the length of a[]
{
    if (n < 2)
        return;     //BASE CASE
    int mid = n / 2;
    int left[mid];
    int right[n-mid];

    for (int i = 0; i < mid; i++)
        left[i] = a[i];

    for (int i = mid; i < n; i++)
        right[i-mid] = a[i];

    mergesort(left, mid);
    mergesort(right, n - mid);
    merge(a, left, right, mid, n - mid); 
}

这包括对您的主要问题的修复——加载right数组的循环使最后一个元素未被复制。

具有调试功能,例如:

void dump_array(const char *tag, int n, int *a)
{
    printf("%s:%d:", tag, n);
    for (int i = 0; i < n; i++)
        printf(" %3d", a[i]);
    putchar('\n');
}

您可以通过以下方式进行大量有效调试:

void mergesort(int a[],int n)
{
    if (n < 2)
        return;
    int mid = n / 2;
    int left[mid];
    int right[n-mid];

    dump_array("-->>mergesort()", n, a);

    for (int i = 0; i < mid; i++)
        left[i] = a[i];
    dump_array("left", mid, left);

    for (int i = mid; i < n; i++)
        right[i-mid] = a[i];
    dump_array("right", n - mid, right);

    mergesort(left, mid);
    dump_array("merged-L", mid, left);
    mergesort(right, n - mid);
    dump_array("merged-R", n - mid, right);
    merge(a, left, right, mid, n - mid);
    dump_array("<<--mergesort()", n, a);
}

在您的代码中,带有标记的输出right将显示最后一个元素的 0 或半随机数据,而不是您所期望的。这将暗示问题出在哪里。保留dump_array()功能;拥有它是一种有用的生物。这是一个简单的版本;例如,您可以发明更复杂的版本,在长数组的中间位置输出换行符。

于 2013-09-24T05:01:28.110 回答
2

问题出在以下代码中:

   for(int i = mid; i < n-1; i++)
   {
        right[i-mid] = a[i];
   }

它应该是:

   for(int i = mid; i < n; i++) // right should range from mid to n - 1 *inclusive*
   {
        right[i-mid] = a[i];
   }
于 2013-09-24T03:05:14.023 回答
0

这是合并排序的简单实现,没有任何复杂性。只需传递数组指针和数组中的整数总数。

void merge(int *a, int top)// Array pointer and max entries
{
    int l1, k, l2, u1, u2, size = 1, i, j;
    int *sa;
    sa = (int *)calloc(top, sizeof(int));

    while (size < top)
    {
        l1 = 0;
        k = 0;
        while (l1 + size < top)
        {
            l2 = l1 + size;
            u1 = l2 - 1;
            u2 = ((l2 + size - 1) < top ? l2 + size - 1 : top - 1);

            for (i = l1, j = l2; i <= u1 && j <= u2; )// Merging
            {
                sa[k++] = a[i] <= a[j] ? a[i++] : a[j++];
            }
            for ( ; i <= u1; )
                sa[k++] = a[i++];
            for ( ; j <= u2; )
                sa[k++] = a[j++];
            l1 = u2 + 1;
        }

        for (i = l1; i < top; i++) // For the left outs of the process
            sa[k++] = a[i];

        for (i = 0; i < top; i++)
            a[i] = sa[i];

        size *= 2;
    }
}
于 2013-11-08T20:37:02.933 回答