我在我的 C 二进制搜索代码中做错了什么? (迭代和递归)
What am I doing wrong in my C binary search code? (iterative & recursive)
我在这里做错了什么?
原型是不可更改的。
我需要编写这两个函数,这就是我写的。
他们 90% 的时间都在工作。当我尝试搜索中间元素时,迭代方式出现问题。
递归方式出现了一些问题,但我真的不知道哪里出了问题。有时它会找到元素,有时找不到。
我无法更改的另一件事是我必须 避免检查函数内部是否array[middle] == key
。老师希望我们只在我退出循环时才做这个检查,他希望循环只检查我应该向右还是向左。
int *BinarySearchIter(const int SortedArray[], int key, size_t length)
{
/* variables that will set the boundries of the searched area */
int left_index = 0, right_index = 0, middle = 0;
int *result = NULL;
assert(SortedArray);
assert(length);
left_index = 0; /* first elemenet in the array */
right_index = length - 1; /* last elemenet in the array */
/* while only one elemenet is left in the searched area */
while (left_index <= right_index)
{
/* split it to half */
middle = (left_index + right_index) / 2;
/* if key greater, ignore left half, search only in right half */
if (SortedArray[middle] < key)
{
left_index = middle + 1;
}
/* if key smaller, ignore right half, search only in left half */
else
{
right_index = middle - 1;
}
}
/* if we reach here, then element is the key or was not found */
result = (int *)SortedArray + middle;
return (key == *result ? result : NULL);
}
/******************************************************************************/
int *BinarySearchRec(const int SortedArray[], int key, size_t length)
{
int left_index = 0; /* first elemenet of the array */
int right_index = length - 1; /* last elemenet in the array */
int middle = 0, isBigger = 0;
if (1 == length)
{
return (key == *SortedArray ? (int *)SortedArray : NULL);
}
middle = (left_index + right_index) / 2;
isBigger = (key > SortedArray[middle]);
return (BinarySearchRec(SortedArray+(middle + 1)*isBigger, key, isBigger * (length - length /2 ) + !isBigger*(length/2)));
}
/******************************************************************************/
我正在尝试运行以下测试;
const int sorted_arr[] = { 2, 4, 8, 10, 12};
size_t arr_length = sizeof(sorted_arr) / sizeof(sorted_arr[0]), i = 0;
int key_to_find = 0;
int *iterative_res = NULL;
int *recursive_res = NULL;
for (i = 0; i < arr_length; ++i)
{
key_to_find = sorted_arr[i];
iterative_res = BinarySearchIter(sorted_arr, key_to_find, arr_length);
recursive_res = BinarySearchRec(sorted_arr, key_to_find, arr_length);
Print if any errors (nulls or key doesn't match any of the results)
}
这是测试的输出:
ERRORS:
Needs to find: 8
But found:
Iterative: NULL (failure)
Recursive: NULL (failure)
Needs to find: 12
But found:
Iterative: 12 (success)
Recursive: NULL (failure)
对于迭代函数
让我们想想你的代码在做什么。您有一个包含 5 个元素的数组,假设您正在搜索 8.
2 4 8 10 12
^
在第一次迭代中,值是这样的:
left_index = 0, right_index = 4, middle_index = 2
在 if 语句中,程序检查 (SortedArray[middle_index] < key) 这是错误的,因为 8 不大于 8。所以它执行else 语句。错误就在这里。
在 else 语句中,您将丢弃中间元素。但在这种情况下,中间元素是您要查找的键。
因此,您需要更改的第一件事就像 @Eric Postpischil 所说的那样,将您的 else 语句更改为:
else {
right_index = middle;
}
让我们继续第二次迭代:
2 4 8
^
left_index = 0, right_index = 2, middle_index = 1
第二次迭代后,第三次迭代将只包含 1 个元素。
8
left_index = 2, right_index = 2, middle_index = 2
此时程序因为一直执行else语句而陷入死循环。左右索引始终保持不变。
因此,您需要做的第二个更改是将 while 条件更改为:
while (left_index < right_index)
此时当左索引和右索引相等时,循环就会中断。 您需要做的最后一件事是再次更新您的中间索引。或者您可以使用 left_index 或 right_index(它们相等并不重要)。
最后,您的函数应如下所示:
const int *BinarySearchIter(const int SortedArray[], int key, size_t length) {
int left_index = 0, right_index = length - 1;
while (left_index < right_index) {
int middle = (left_index + right_index) / 2;
if (SortedArray[middle] < key) {
left_index = middle + 1;
} else {
right_index = middle;
}
}
return SortedArray[right_index] == key ? SortedArray + right_index : NULL;
}
对于递归函数
您唯一需要更改的是:
return BinarySearchRec(SortedArray+(middle + 1)*isBigger, key, (length / 2) + !isBigger * (length % 2));
原因同迭代函数。您需要包括中间元素。对于数组的下半部分,数组长度需要等于 ceil(length / 2),而对于数组的上半部分,它需要等于 地板(长度/2).
您也可以这样做:
const int *BinarySearchRec(const int SortedArray[], int key, size_t length) {
if (1 == length) return (key == *SortedArray ? SortedArray : NULL);
int middle = (length + 1) / 2;
if (key >= SortedArray[middle]) return BinarySearchRec(SortedArray + middle, key, length / 2);
else return BinarySearchRec(SortedArray, key, middle);
}
我在这里做错了什么? 原型是不可更改的。 我需要编写这两个函数,这就是我写的。 他们 90% 的时间都在工作。当我尝试搜索中间元素时,迭代方式出现问题。
递归方式出现了一些问题,但我真的不知道哪里出了问题。有时它会找到元素,有时找不到。
我无法更改的另一件事是我必须 避免检查函数内部是否array[middle] == key
。老师希望我们只在我退出循环时才做这个检查,他希望循环只检查我应该向右还是向左。
int *BinarySearchIter(const int SortedArray[], int key, size_t length)
{
/* variables that will set the boundries of the searched area */
int left_index = 0, right_index = 0, middle = 0;
int *result = NULL;
assert(SortedArray);
assert(length);
left_index = 0; /* first elemenet in the array */
right_index = length - 1; /* last elemenet in the array */
/* while only one elemenet is left in the searched area */
while (left_index <= right_index)
{
/* split it to half */
middle = (left_index + right_index) / 2;
/* if key greater, ignore left half, search only in right half */
if (SortedArray[middle] < key)
{
left_index = middle + 1;
}
/* if key smaller, ignore right half, search only in left half */
else
{
right_index = middle - 1;
}
}
/* if we reach here, then element is the key or was not found */
result = (int *)SortedArray + middle;
return (key == *result ? result : NULL);
}
/******************************************************************************/
int *BinarySearchRec(const int SortedArray[], int key, size_t length)
{
int left_index = 0; /* first elemenet of the array */
int right_index = length - 1; /* last elemenet in the array */
int middle = 0, isBigger = 0;
if (1 == length)
{
return (key == *SortedArray ? (int *)SortedArray : NULL);
}
middle = (left_index + right_index) / 2;
isBigger = (key > SortedArray[middle]);
return (BinarySearchRec(SortedArray+(middle + 1)*isBigger, key, isBigger * (length - length /2 ) + !isBigger*(length/2)));
}
/******************************************************************************/
我正在尝试运行以下测试;
const int sorted_arr[] = { 2, 4, 8, 10, 12};
size_t arr_length = sizeof(sorted_arr) / sizeof(sorted_arr[0]), i = 0;
int key_to_find = 0;
int *iterative_res = NULL;
int *recursive_res = NULL;
for (i = 0; i < arr_length; ++i)
{
key_to_find = sorted_arr[i];
iterative_res = BinarySearchIter(sorted_arr, key_to_find, arr_length);
recursive_res = BinarySearchRec(sorted_arr, key_to_find, arr_length);
Print if any errors (nulls or key doesn't match any of the results)
}
这是测试的输出:
ERRORS:
Needs to find: 8
But found:
Iterative: NULL (failure)
Recursive: NULL (failure)
Needs to find: 12
But found:
Iterative: 12 (success)
Recursive: NULL (failure)
对于迭代函数
让我们想想你的代码在做什么。您有一个包含 5 个元素的数组,假设您正在搜索 8.
2 4 8 10 12
^
在第一次迭代中,值是这样的:
left_index = 0, right_index = 4, middle_index = 2
在 if 语句中,程序检查 (SortedArray[middle_index] < key) 这是错误的,因为 8 不大于 8。所以它执行else 语句。错误就在这里。
在 else 语句中,您将丢弃中间元素。但在这种情况下,中间元素是您要查找的键。
因此,您需要更改的第一件事就像 @Eric Postpischil 所说的那样,将您的 else 语句更改为:
else {
right_index = middle;
}
让我们继续第二次迭代:
2 4 8
^
left_index = 0, right_index = 2, middle_index = 1
第二次迭代后,第三次迭代将只包含 1 个元素。
8
left_index = 2, right_index = 2, middle_index = 2
此时程序因为一直执行else语句而陷入死循环。左右索引始终保持不变。
因此,您需要做的第二个更改是将 while 条件更改为:
while (left_index < right_index)
此时当左索引和右索引相等时,循环就会中断。 您需要做的最后一件事是再次更新您的中间索引。或者您可以使用 left_index 或 right_index(它们相等并不重要)。
最后,您的函数应如下所示:
const int *BinarySearchIter(const int SortedArray[], int key, size_t length) {
int left_index = 0, right_index = length - 1;
while (left_index < right_index) {
int middle = (left_index + right_index) / 2;
if (SortedArray[middle] < key) {
left_index = middle + 1;
} else {
right_index = middle;
}
}
return SortedArray[right_index] == key ? SortedArray + right_index : NULL;
}
对于递归函数
您唯一需要更改的是:
return BinarySearchRec(SortedArray+(middle + 1)*isBigger, key, (length / 2) + !isBigger * (length % 2));
原因同迭代函数。您需要包括中间元素。对于数组的下半部分,数组长度需要等于 ceil(length / 2),而对于数组的上半部分,它需要等于 地板(长度/2).
您也可以这样做:
const int *BinarySearchRec(const int SortedArray[], int key, size_t length) {
if (1 == length) return (key == *SortedArray ? SortedArray : NULL);
int middle = (length + 1) / 2;
if (key >= SortedArray[middle]) return BinarySearchRec(SortedArray + middle, key, length / 2);
else return BinarySearchRec(SortedArray, key, middle);
}