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

时间:2013-09-23 23:25:16

标签: c algorithm sorting mergesort

我正在尝试在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跟踪我的代码,但我仍然无法对其进行排序。

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

3 个答案:

答案 0 :(得分: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()功能;它是一个有用的生物。这是一个简单的版本;例如,你可以发明更复杂的版本,在长数组的中间位置输出换行符。

答案 1 :(得分: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];
   }

答案 2 :(得分:-1)

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

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

 }