我正在尝试在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跟踪我的代码,但我仍然无法对其进行排序。
是否有人对可能发生的事情有任何想法?
答案 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;
}
}