1
int recurbinarysearch(int * oringalary, int low, int high, int target) {

    if (low > high) {
        return -1;
    } else {
        int mid = (low + high) / 2;
        if (target == * (oringalary + mid)) return mid;
        if (target > * (oringalary + mid)) return recurbinarysearch(oringalary, mid + 1, high, target);
        if (target < * (oringalary + mid)) return recurbinarysearch(oringalary, low, mid - 1, target);
    }
}

有人在我的递归二进制搜索算法中看到错误吗?偶尔它会返回一个不正确的索引(通常它会偏离一个),但偶尔会偏离。然而,通常它是正确的。我没有看到问题,将不胜感激。

4

2 回答 2

1

编辑 这是被接受的,所以我想我真的应该试着把它变成一个正确的答案。

我最初假设(请参阅下面的“注意”)问题是使用半开边界。它实际上(正确地)使用了包容性边界。在最初的通话中,low=0high=n-1.

使用包容性边界通常被认为是一件坏事 - 请参阅 Dijkstra 的经典著作 (PDF)。在 C 系列语言中,半开边界是一种常见的约定,甚至是对for (i = 0; i < n; i++)over的偏好for (i = 0; i <= n-1; i++)。但是,鉴于使用了包含边界,问题中的代码似乎是正确的。

然而,正如 WhozCraig 在评论中所发现的那样,调用代码不遵守该约定,并且传递了错误的边界 - 包括搜索范围内的越界垃圾项。因为那个额外的项目是垃圾,所以范围内的项目已排序的假设也可能无效。大多数搜索不会找到该垃圾项(因为您不太可能搜索它具有的任何垃圾值),但它会误导搜索。


注意 这可能不是答案,但评论太长了。

你的界限是包容的、排他的还是半开放的?

我将假设 半开放 - 包含 at low,独占 at high。如果是这样,这条线看起来是错误的......

if (target < * (oringalary + mid))
    return recurbinarysearch(oringalary, low, mid - 1, target);

原因是您已在 处检查了该项目mid,但您将mid - 1其用作新的专属上限。这意味着mid - 1未检查的位于 的项目已意外从搜索中排除。线路应该...

if (target < * (oringalary + mid))
    return recurbinarysearch(oringalary, low, mid, target);

这将项目保持mid - 1在要搜索的范围内。mid由于上限是独占的,因此不会再次搜索该项目。

在二进制搜索中搞乱界限是一个常见问题,它会导致比看起来应该的错误更多。

但是,这本身并不能解释您的症状 - 它有时应该无法找到项目(可能大约 50% 的搜索),但它不应该为成功的搜索报告错误的位置。

二进制搜索中错误边界的常见症状是无限循环(同一项目被重复检查,因为它没有被排除在边界之外)或搜索未能找到存在的项目(因为项目被排除在搜索范围之外)检查)。

老实说,我看不出你的症状是如何发生的。函数退出的所有可能方式都应该给出正确的成功结果,否则给出-1失败结果。我能想到的唯一可能的例外是在这段代码之外——误解了结果,例如未能检查-1结果。

顺便说一句 - 这是我代表我的问题和回答关于一次比较每次迭代二分搜索的绝佳机会。

编辑我想我发现了边界的另一个问题 - 仍然假设半开,这条线是错误的......

if (low > high) {

并且应该...

if (low >= high) {

原因是对于半开边界,如果边界相等,则中间没有要检查的项目——即使是下限项目也是无效的,因为上限等于它并且是互斥的。这使您仍然可以测试

于 2013-02-19T05:01:46.373 回答
1

有关二分搜索的全面讨论,请研究 Jon Bentley 的Programming Pearls

这是您的代码的测试工具,非常受 Programming Pearls 的启发,以及您的代码的检测版本。我所做的唯一更改是在二进制搜索中添加(现已注释掉)调试打印。测试代码的输出几乎是完美的(工具表明一切都通过了,但并不完全正确):

N =  0: 
search for  0 in  0 entries - returned  0 found  0 PASS
N =  1: [0] = 1;
search for  0 in  1 entries - returned -1          PASS
search for  1 in  1 entries - returned  0 found  1 PASS
search for  2 in  1 entries - returned -1          PASS
N =  2: [0] = 1;[1] = 3;
search for  0 in  2 entries - returned -1          PASS
search for  1 in  2 entries - returned  0 found  1 PASS
search for  2 in  2 entries - returned -1          PASS
search for  3 in  2 entries - returned  1 found  3 PASS
search for  4 in  2 entries - returned -1          PASS
N =  3: [0] = 1;[1] = 3;[2] = 5;
search for  0 in  3 entries - returned -1          PASS
search for  1 in  3 entries - returned  0 found  1 PASS
search for  2 in  3 entries - returned -1          PASS
search for  3 in  3 entries - returned  1 found  3 PASS
search for  4 in  3 entries - returned -1          PASS
search for  5 in  3 entries - returned  2 found  5 PASS
search for  6 in  3 entries - returned -1          PASS
N =  4: [0] = 1;[1] = 3;[2] = 5;[3] = 7;
search for  0 in  4 entries - returned -1          PASS
search for  1 in  4 entries - returned  0 found  1 PASS
search for  2 in  4 entries - returned -1          PASS
search for  3 in  4 entries - returned  1 found  3 PASS
search for  4 in  4 entries - returned -1          PASS
search for  5 in  4 entries - returned  2 found  5 PASS
search for  6 in  4 entries - returned -1          PASS
search for  7 in  4 entries - returned  3 found  7 PASS
search for  8 in  4 entries - returned -1          PASS
N =  5: [0] = 1;[1] = 3;[2] = 5;[3] = 7;[4] = 9;
search for  0 in  5 entries - returned -1          PASS
search for  1 in  5 entries - returned  0 found  1 PASS
search for  2 in  5 entries - returned -1          PASS
search for  3 in  5 entries - returned  1 found  3 PASS
search for  4 in  5 entries - returned -1          PASS
search for  5 in  5 entries - returned  2 found  5 PASS
search for  6 in  5 entries - returned -1          PASS
search for  7 in  5 entries - returned  3 found  7 PASS
search for  8 in  5 entries - returned -1          PASS
search for  9 in  5 entries - returned  4 found  9 PASS
search for 10 in  5 entries - returned -1          PASS
N =  6: [0] = 1;[1] = 3;[2] = 5;[3] = 7;[4] = 9;[5] = 11;
search for  0 in  6 entries - returned -1          PASS
search for  1 in  6 entries - returned  0 found  1 PASS
search for  2 in  6 entries - returned -1          PASS
search for  3 in  6 entries - returned  1 found  3 PASS
search for  4 in  6 entries - returned -1          PASS
search for  5 in  6 entries - returned  2 found  5 PASS
search for  6 in  6 entries - returned -1          PASS
search for  7 in  6 entries - returned  3 found  7 PASS
search for  8 in  6 entries - returned -1          PASS
search for  9 in  6 entries - returned  4 found  9 PASS
search for 10 in  6 entries - returned -1          PASS
search for 11 in  6 entries - returned  5 found 11 PASS
search for 12 in  6 entries - returned -1          PASS
N =  7: [0] = 1;[1] = 3;[2] = 5;[3] = 7;[4] = 9;[5] = 11;[6] = 13;
search for  0 in  7 entries - returned -1          PASS
search for  1 in  7 entries - returned  0 found  1 PASS
search for  2 in  7 entries - returned -1          PASS
search for  3 in  7 entries - returned  1 found  3 PASS
search for  4 in  7 entries - returned -1          PASS
search for  5 in  7 entries - returned  2 found  5 PASS
search for  6 in  7 entries - returned -1          PASS
search for  7 in  7 entries - returned  3 found  7 PASS
search for  8 in  7 entries - returned -1          PASS
search for  9 in  7 entries - returned  4 found  9 PASS
search for 10 in  7 entries - returned -1          PASS
search for 11 in  7 entries - returned  5 found 11 PASS
search for 12 in  7 entries - returned -1          PASS
search for 13 in  7 entries - returned  6 found 13 PASS
search for 14 in  7 entries - returned -1          PASS
N =  8: [0] = 1;[1] = 3;[2] = 5;[3] = 7;[4] = 9;[5] = 11;[6] = 13;[7] = 15;
search for  0 in  8 entries - returned -1          PASS
search for  1 in  8 entries - returned  0 found  1 PASS
search for  2 in  8 entries - returned -1          PASS
search for  3 in  8 entries - returned  1 found  3 PASS
search for  4 in  8 entries - returned -1          PASS
search for  5 in  8 entries - returned  2 found  5 PASS
search for  6 in  8 entries - returned -1          PASS
search for  7 in  8 entries - returned  3 found  7 PASS
search for  8 in  8 entries - returned -1          PASS
search for  9 in  8 entries - returned  4 found  9 PASS
search for 10 in  8 entries - returned -1          PASS
search for 11 in  8 entries - returned  5 found 11 PASS
search for 12 in  8 entries - returned -1          PASS
search for 13 in  8 entries - returned  6 found 13 PASS
search for 14 in  8 entries - returned -1          PASS
search for 15 in  8 entries - returned  7 found 15 PASS
search for 16 in  8 entries - returned -1          PASS
N =  9: [0] = 1;[1] = 3;[2] = 5;[3] = 7;[4] = 9;[5] = 11;[6] = 13;[7] = 15;[8] = 17;
search for  0 in  9 entries - returned -1          PASS
search for  1 in  9 entries - returned  0 found  1 PASS
search for  2 in  9 entries - returned -1          PASS
search for  3 in  9 entries - returned  1 found  3 PASS
search for  4 in  9 entries - returned -1          PASS
search for  5 in  9 entries - returned  2 found  5 PASS
search for  6 in  9 entries - returned -1          PASS
search for  7 in  9 entries - returned  3 found  7 PASS
search for  8 in  9 entries - returned -1          PASS
search for  9 in  9 entries - returned  4 found  9 PASS
search for 10 in  9 entries - returned -1          PASS
search for 11 in  9 entries - returned  5 found 11 PASS
search for 12 in  9 entries - returned -1          PASS
search for 13 in  9 entries - returned  6 found 13 PASS
search for 14 in  9 entries - returned -1          PASS
search for 15 in  9 entries - returned  7 found 15 PASS
search for 16 in  9 entries - returned -1          PASS
search for 17 in  9 entries - returned  8 found 17 PASS
search for 18 in  9 entries - returned -1          PASS

几乎所有这些都很好。唯一有问题的孩子是第一次搜索,它应该会失败,因为在空数组中不应该找到任何值。

测试代码为:

#include <stdio.h>

int recurbinarysearch(int *oringalary, int low, int high, int target)
{
    //printf("-->> %d..%d: ", low, high);
    if (low > high)
    {
        //printf("<<-- %d ", -1);
        return -1;
    }
    else
    {
        int mid = (low + high) / 2;
        if (target == * (oringalary + mid))
        {
            //printf("<<-- %d ", mid);
            return mid;
        }
        if (target > * (oringalary + mid))
        {
            int r = recurbinarysearch(oringalary, mid + 1, high, target);
            //printf("<<-- %d ", r);
            return r;
        }
        if (target < * (oringalary + mid))
        {
            int r = recurbinarysearch(oringalary, low, mid - 1, target);
            //printf("<<-- %d ", r);
            return r;
        }
    }
}

int main(void)
{
    for (int i = 0; i < 10; i++)
    {
        int a[i+1]; // No zero-size arrays in C
        printf("N = %2d: ", i);
        for (int j = 0; j < i; j++)
        {
            a[j] = 2 * j + 1;
            printf("[%d] = %d;",j, a[j]);
        }
        putchar('\n');

        for (int j = 0; j < 2*i+1; j++)
        {
            int f = recurbinarysearch(a, 0, i, j);
            //putchar('\n');  // debug
            printf("search for %2d in %2d entries - returned %2d",
                    j, i, f);
            if (f >= 0 && f <= i)
            {
                printf(" found %2d", a[f]);
                printf(" %s", (a[f] == j) ? "PASS" : "FAIL");
            }
            else
                printf(" %8s %s", "", (j % 2 == 0) ? "PASS" : "FAIL");
            putchar('\n');
        }
    }
    return(0);
}

我将让您解决如何处理空数组的情况。

于 2013-02-19T06:31:05.283 回答