我在C语言中调用归并排序的递归函数时遇到了问题。

huangapple go评论79阅读模式
英文:

I am facing problems while calling recursive function in merge sort in C Language

问题

我基本上想要实现归并排序,而不创建额外的数组。但是当我递归调用我的函数时,gcc会给出分段错误错误。
以下是我的代码

右循环移位

void rightCircularShift(int *a, int startPos, int endPos)
{
	int temp, j;
	temp = a[endPos - 1];
	for (j = endPos - 1; j > startPos; j--) {
		a[j] = a[j - 1];
	}
	a[startPos] = temp;
}

合并

void merge(int *a, int l, int r, int m)
{
	int leftCounter = l, elementsMoved = 0;
	int rightCounter = m;
	while (elementsMoved < r) {
		if (leftCounter != r || rightCounter != r) {
			if (a[leftCounter] < a[rightCounter]) {
				leftCounter++;
			} else {
				rightCircularShift(a, leftCounter, rightCounter + 1);
				leftCounter++;
				rightCounter++;
			}
		} else {
			break;
		}
		elementsMoved++;
	}
}

主函数

int main()
{
	int n, i, *array;
	system("clear");
	printf("\nEnter number of elements: ");
	scanf("%d", &n);
	array = (int *)malloc(sizeof(int) * n);
	printf("Enter value of elements:\n");
	for (i = 0; i < n; i++) {
		scanf("%d", &array[i]);
	}
	printf("\n");
	mergeSort(array, 0, n - 1);
	printf("\n");
	for (i = 0; i < n; i++) {
		printf("%d\t", array[i]);
	}
	printf("\n");
	return 0;
}

归并排序

void mergeSort(int *a, int left, int right)
{
	int mid = (left + right + 1) / 2;
	if (left < right) {	
		mergeSort(a, left, mid);
		mergeSort(a, mid + 1, right);
		merge(a, left, right, mid);
	}	
}

我已确保其余的函数如循环移位和合并在测试用例中正常工作,但我遇到了这个错误:

我在C语言中调用归并排序的递归函数时遇到了问题。

英文:

I basically want to implement merge sort without creating an extra array. But when I call my function recursively gcc gives segmentation fault error
Here is my code

Right Circular Shift

void rightCircularShift(int *a, int startPos, int endPos)
{
	int temp, j;
	temp = a[endPos - 1];
	for (j = endPos - 1; j > startPos; j--) {
		a[j] = a[j - 1];
	}
	a[startPos] = temp;
}

Merge

void merge(int *a, int l, int r, int m)
{
	int leftCounter = l, elementsMoved = 0;
	int rightCounter = m;
	while (elementsMoved < r) {
		if (leftCounter != r || rightCounter != r) {
			if (a[leftCounter] < a[rightCounter]) {
				leftCounter++;
			} else {
				rightCircularShift(a, leftCounter, rightCounter + 1);
				leftCounter++;
				rightCounter++;
			}
		} else {
			break;
		}
		elementsMoved++;
	}
}

Main

int main()
{
	int n, i, *array;
	system("clear");
	printf("\nEnter number of elements: ");
	scanf("%d", &n);
	array = (int *)malloc(sizeof(int) * n);
	printf("Enter value of elements:\n");
	for (i = 0; i < n; i++) {
		scanf("%d", &array[i]);
	}
	printf("\n");
	mergeSort(array, 0, n - 1);
	printf("\n");
	for (i = 0; i < n; i++) {
		printf("%d\t", array[i]);
	}
	printf("\n");
	return 0;
}

Merge Sort

void mergeSort(int *a, int left, int right)
{
	int mid = (left + right + 1) / 2;
	if (left < right) {	
		mergeSort(a, left, mid);
		mergeSort(a, mid + 1, right);
		merge(a, left, right, mid);
	}	
}

I have ensured the rest of the functions like circular shift and merge are working properly with test cases, but I get this error:

我在C语言中调用归并排序的递归函数时遇到了问题。

答案1

得分: 0

代码部分已被排除,以下是已翻译的内容:

mergeSort 函数中存在一个问题:您计算 mid 索引时出现错误,对于包含2个元素的切片,mid 将等于 right,而调用 mergeSort(a, mid + 1, right) 将导致未定义的行为,因为 mid + 1 > right,这是该函数无法处理的条件。

您应该使用 int mid = (left + right) / 2; 或更好的方法:

int mid = left + (right - left) / 2;  // 防止潜在溢出

merge 函数中也存在一个问题:测试 elementsMoved < r 是不正确的,因为 r 是最后一个元素的索引,而 elementsMoved 表示迭代次数。迭代次数不应超过 r - l + 1 次,但该测试未实现这一点。

您的代码中使用了两种不同的约定:

  • endPos 是在 void rightCircularShift(int *a, int startPos, int endPos) 中表示切片末尾的索引
  • right 是在 void mergeSort(int *a, int left, int right) 中表示切片的最后一个元素的索引

使用第一种约定会更少令人困惑,这在 C 语言中是常规的,其中数组索引从 0 开始。

此外,请注意,在 if (a[leftCounter] <= a[rightCounter]) 中必须使用 <= 以使排序稳定。

以下是经过修改的版本:

#include <stdio.h>
#include <stdlib.h>

void rightCircularShift(int *a, int startPos, int endPos) {
    if (startPos < endPos) {
        int temp = a[endPos - 1];
        for (int j = endPos - 1; j > startPos; j--) {
            a[j] = a[j - 1];
        }
        a[startPos] = temp;
    }
}

void merge(int *a, int left, int mid, int right) {
    int leftCounter = left;
    int rightCounter = mid;
    while (leftCounter < mid && rightCounter < right) {
        if (a[leftCounter] <= a[rightCounter]) {
            leftCounter++;
        } else {
            rightCircularShift(a, leftCounter, rightCounter + 1);
            leftCounter++;
            rightCounter++;
            mid++;
        }
    }
}

void mergeSort(int *a, int left, int right) {
    if (right - left > 1) {
        // mid 是第二个切片的第一个元素的索引
        int mid = left + (right - left) / 2;
        mergeSort(a, left, mid);
        mergeSort(a, mid, right);
        merge(a, left, mid, right);
    }
}

int main() {
    int n, i, *array;
    system("clear");
    printf("\nEnter number of elements: ");
    if (scanf("%d", &n) != 1 || n <= 0)
        return 1;
    array = malloc(sizeof(*array) * n);
    if (array == NULL)
        return 1;
    printf("Enter value of elements:\n");
    for (i = 0; i < n; i++) {
        if (scanf("%d", &array[i]) != 1)
            return 1;
    }
    printf("\n");
    mergeSort(array, 0, n);
    printf("\n");
    for (i = 0; i < n; i++) {
        printf("%d\t", array[i]);
    }
    printf("\n");
    free(array);
    return 0;
}

但需要注意,使用这种方式实现归并排序非常低效:所需的空间受限于 O(log(N)),对于平均时间复杂度来说,它可能爆炸到 O(N^2) 或更糟,特别是对于测试的示例,即按递减顺序排序的数组。

有一些方法可以以有限的内存实现归并排序,同时限制这个缺点,但它们比这种简单的方法更复杂。

英文:

There is a problem in the mergeSort function: you compute the mid index incorrectly, for a slice of 2 elements, mid will be equal to right and the call mergeSort(a, mid + 1, right) will have undefined behavior because mid + 1 &gt; right, a condition the function cannot handle.

You should use int mid = (left + right) / 2; or better:

    int mid = left + (right - left) / 2;  // prevent potential overflow

There is also a problem in the merge function: the test elementsMoved &lt; r is incorrect as r is the index of the last element and elementsMoved the number of iterations. It does not make sense to iterate more than r - l + 1 times, but the test does not implement that.

You use 2 different conventions in your code:

  • endPos is the index past the end of the slice in void rightCircularShift(int *a, int startPos, int endPos)
  • right is the index of the last element of the slice in void mergeSort(int *a, int left, int right)

It would be less confusing to always use the first convention, which is customary in C, where array indexes start at 0.

Also not that you must use &lt;= in if (a[leftCounter] &lt;= a[rightCounter]) to make the sort stable.

Here is a modified version:

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
void rightCircularShift(int *a, int startPos, int endPos) {
if (startPos &lt; endPos) {
int temp = a[endPos - 1];
for (int j = endPos - 1; j &gt; startPos; j--) {
a[j] = a[j - 1];
}
a[startPos] = temp;
}
}
void merge(int *a, int left, int mid, int right) {
int leftCounter = left;
int rightCounter = mid;
while (leftCounter &lt; mid &amp;&amp; rightCounter &lt; right) {
if (a[leftCounter] &lt;= a[rightCounter]) {
leftCounter++;
} else {
rightCircularShift(a, leftCounter, rightCounter + 1);
leftCounter++;
rightCounter++;
mid++;
}
}
}
void mergeSort(int *a, int left, int right) {
if (right - left &gt; 1) {
// mid is the index of the first element of the second slice
int mid = left + (right - left) / 2;
mergeSort(a, left, mid);
mergeSort(a, mid, right);
merge(a, left, mid, right);
}
}
int main() {
int n, i, *array;
system(&quot;clear&quot;);
printf(&quot;\nEnter number of elements: &quot;);
if (scanf(&quot;%d&quot;, &amp;n) != 1 || n &lt;= 0)
return 1;
array = malloc(sizeof(*array) * n);
if (array == NULL)
return 1;
printf(&quot;Enter value of elements:\n&quot;);
for (i = 0; i &lt; n; i++) {
if (scanf(&quot;%d&quot;, &amp;array[i]) != 1)
return 1;
}
printf(&quot;\n&quot;);
mergeSort(array, 0, n);
printf(&quot;\n&quot;);
for (i = 0; i &lt; n; i++) {
printf(&quot;%d\t&quot;, array[i]);
}
printf(&quot;\n&quot;);
free(array);
return 0;
}

Note however that implementing merge sort this way is very inefficient: the space needed is limited to O(log(N)), corresponding to the recursion depth, but the average time complexity explodes to O(N<sup>2</sup>) or worse, especially for the very example tested: an array sorted in decreasing order.

There are ways to implement merge sort with limited amount of memory while limiting this downside, but they are more complicated than this simple approach.

huangapple
  • 本文由 发表于 2023年2月18日 13:57:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/75491489.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定