尝试在C中设置合并排序递归函数,我想出了以下内容。
奇怪的是,当数组的大小很小(大约10)时,它的工作原理非常好。对于10到15的大小,它有时会错误地排序(一个或两个值随机放置在最终数组中),对于高于15的值,它总是错误地排序一个或两个值,并用一个或两个整数值替换非常大的负整数。
例如,这个数组:[3] [9] [2] [11] [8] [7] [5] [2]
按类型排序:[2] [2] [3] [-254587859] [7] [8] [11]
-
以下是我提出的代码:
main():
int main(int ac, char **av)
{
int size = atoi(av[1]);
int *array = malloc(size*sizeof(int));
int i;
for (i = 0; i < size; i++) { array[i] = rand() % size; }
merge_sort(array, 0, size-1);
print_array(array, size);
free(array);
return 0;
}
merge_sort():
void merge_sort(int array[], int beg, int end)
{
int mid = (end + beg) / 2;
if (beg < end)
{
merge_sort(array, beg, mid);
merge_sort(array, mid+1, end);
merge(array, beg, mid, end);
}
return;
}
merge():
void merge(int array[], int beg, int mid, int end)
{
int size_left = mid - beg + 1;
int size_right = end - mid;
int *left = malloc((size_left)*sizeof(int));
int *right = malloc((size_right)*sizeof(int));
int i,j,k;
for (i = 0; i < size_left; i++) { left[i] = array[beg+i]; }
for (j = 0; j < size_right; j++) { right[j] = array[mid+1+j]; }
i = 0; j = 0; for (k = beg; k <= end; k++) { array[k] = (left[i] <= right[j]) ? left[i++] : right[j++]; }
free(left); free(right);
return;
}
我想这是一个内存分配问题,我可以分配内存负载(我试过,但它可以工作),但这不是重点。你知道那里发生了什么吗?
配置:gcc 4.6.2,Windows 7 64位。
答案 0 :(得分:4)
问题在于合并:
array[k] = (left[i] <= right[j]) ? left[i++] : right[j++];
这并不能解释i
或j
可能超过数组末尾的事实。你需要实际检查:
i = j = 0;
k = beg;
// Merge both
while( i < size_left && j < size_right ) {
array[k++] = (left[i] <= right[j]) ? left[i++] : right[j++];
}
// Merge leftovers
while( i < size_left ) array[k++] = left[i++];
while( j < size_right ) array[k++] = left[j++];
答案 1 :(得分:3)
我的猜测是问题是行:
for (int k = beg; k <= end; k++) {
array[k] = (left[i] <= right[j]) ? left[i++] : right[j++];
}
考虑left = [1, 2, 3, 4]
和right = [5, 6, 7, 8]
。左边将被引用直到i = 4
然后你尝试引用超出数组的left[4]
并且具有未确定的值(在Java或其他安全语言中你会得到IndexOutOfBoundException或类似的错误 - 在C中你在你自己,你刚读了一些随机记忆。)
您需要确保i
和j
在数组范围内。例如:
for (int k = beg; k <= end; k++) {
if (i == size_left) {
array[k] = right[j++];
} else if (j == size_right) {
array[k] = left[i++];
} else {
array[k] = (left[i] <= right[j]) ? left[i++] : right[j++];
}
}
不幸的是,这种错误在C语言中很常见。有一些免费和商业工具可以让你找到它们。对于Linux,通常使用Valgrind。 CLang或gcc 4.8.0+ AddressSanitizer也会帮助解决这个问题 - 遗憾的是我不知道除了它之外的任何Windows免费工具。
答案 2 :(得分:0)
好的,谢谢Maciej和Paddy,你在我关于“合并”步骤的推理中指出了这么大的失败。这正是C带来的有趣之处,感觉你是“靠自己”,如果你采取了错误的举动,没有任何指导方针可以阻止你。
根据您的改进,以下是我的结论:
for (k = beg; k <= end; k++) {
array[k] = (left[i] <= right[j]) ?
(i == size_left) ? right[j++] : left[i++] :
(j == size_right) ? left[i++] : right[j++];
}