简体   繁体   English

C中的合并排序算法无法按预期工作

[英]Merge sort algorithm in C not working as expected

I am trying to implement the merge sort algorithm in C. I understand how the algorithm is supposed to work however I am encountering some difficulties with the implementation. 我正在尝试在C中实现合并排序算法。我了解该算法应该如何工作,但是在实现过程中遇到了一些困难。

I understand that there are hundreds of examples and source code for it's implementation but I was hoping someone could help me understand why mine is not working correctly. 我知道有数百个示例及其实现的源代码,但我希望有人可以帮助我理解为什么我的不能正常工作。

My code is below and after the code I explain what I have tried so far. 我的代码在下面,在代码之后,我将解释到目前为止我已经尝试过的内容。

#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;
}  

As it is and with the unsorted array [3,4,1,6] the output is 0 0 1 3 . [3,4,1,6]使用未排序的数组[3,4,1,6] ,输出为0 0 1 3 Clearly the 1 and 3 are in the right order relative to each other but the two zeros at the beginning are clearly wrong. 显然1和3彼此之间的顺序正确,但是开头的两个零显然是错误的。 At first it seemed to me that I was inserting 4 and 6 to the right and out of bounds of the array. 最初,在我看来,我是在数组的右边和右边插入4和6。

I used some print statements to try and debug but I haven't been able to figure out what was going on. 我使用了一些打印语句来尝试和调试,但是我无法弄清楚发生了什么。 I even tried to follow my code with gdb but I still could not sort it. 我什至尝试使用gdb遵循我的代码,但仍然无法对其进行排序。

Does any one have any ideas of what might be happening? 是否有人对可能发生的事情有任何想法?

A more nearly idiomatic way of writing the merge() code would be: 编写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++];
}

That's about half the number of lines of your code, and within broad limits, the less code there is to read, the better. 这大约是代码行数的一半,并且在很宽的范围内,要读取的代码越少越好。 There are those who insist on having braces after each loop or conditional. 有些人坚持在每次循环或条件循环后使用大括号。 I don't think that's necessary (or particularly helpful), but if that's the style you like, you can use it. 我认为这不是必需的(或特别有帮助),但是如果您喜欢这种样式,则可以使用它。

Your mergesort() code is less flabby, but could be changed to: 您的mergesort()代码不那么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); 
}

This includes the fix for your main problem — the loop loading the right array was leaving the last element uncopied. 这包括针对您的主要问题的解决方案-加载right数组的循环未复制最后一个元素。

With a debugging function such as: 具有如下调试功能:

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');
}

You can do a lot of effective debugging with: 您可以使用以下方法进行许多有效的调试:

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);
}

In your code, the output with the tag right would show 0 or semi-random data for the last element, rather than what you're expecting. 在您的代码中,标签为right的输出将显示最后一个元素的0或半随机数据,而不是您期望的结果。 This would be a hint as to where the trouble is. 这将暗示问题出在哪里。 Keep the dump_array() function around; 保留dump_array()函数; it is a useful creature to have. 这是一个有用的生物。 It's a simple-minded version; 这是一个简单的版本; you can invent more complex versions which outputs a newline at intermediate positions for long arrays, for example. 您可以发明更复杂的版本,例如在长数组的中间位置输出换行符。

The issue is in the following code: 问题在以下代码中:

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

It should be: 它应该是:

   for(int i = mid; i < n; i++) // right should range from mid to n - 1 *inclusive*
   {
        right[i-mid] = a[i];
   }

This is simple implementation of merge sort without any complications. 这是合并排序的简单实现,没有任何复杂性。 Just pass the array pointer and total number of entires in the array. 只需传递数组指针和数组中整数的总数即可。

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;
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM