【问题标题】:Where is the mistake in my code to perform Binary Search?我的代码中执行二进制搜索的错误在哪里?
【发布时间】:2016-11-10 12:55:39
【问题描述】:

我正在编写二进制搜索算法的代码。

代码:

#include "cs50.h"

int main(void) {
    int n = GetInt();
    int value = GetInt();
    int values[n];

    for (int i = 0; i < n; i++) {
        printf("Put in number %i ", i + 1);
        values[i] = GetInt();
    }

    int mid = (n - 1) / 2;
    int en = 0;
    int ex = n - 1;

    for (int i = 0, xt = i + 1; i < xt; i++) {
        if (value > values[mid]) {
            en = mid;
            mid = (en + ex) / 2;
        }
        else if (value < values[mid]) {
            ex = mid;
            mid = (en + ex) / 2;
        }
        else if (value == values[mid]) {
            printf("found");
            break;
        } else {
            printf("not found");
            break;
        }
    }
}

但它只在要查找的值位于中间某个位置时才有效。

在以下情况下失败:

  1. 要找到的值是第一个或最后一个。
  2. 要查找的值不在输入的值中。

我真的想不通。

【问题讨论】:

  • 这是一个理想的情况,使用调试器将极大地帮助您。或者甚至一些调试打印语句怎么样?这些只是您在寻求帮助之前应该执行的标准调试 101 步骤。
  • 这是一个很好的调试起点:ericlippert.com/2014/03/05/how-to-debug-small-programs。在这种情况下,我建议删除GetInt 并以您知道出错的最小情况开始values,然后弄清楚会发生什么。
  • int i = 0,xt = i+1; i&lt;xt; 你确定这是你想要的吗?这意味着循环只运行一次。即使是非常基本的调试,您也可以轻松找到这一点。
  • 这可能从不注释代码开始(您使用 看起来 的“计数循环”,二进制搜索对此没有用处),选择惰性变量名称 (@987654326 @ 对于“计数器变量”来说是完全可以的,midn 很容易,但是 enex 是在自找麻烦(lo&hi 会很懒惰,但是使用 IDE无处不在,使用low&high)) 似乎每个人都过得更好,并将有趣的分支放入 if-else if-else 链:>、和 else? (everything seems alright - 根据?)
  • 你认为在什么情况下会打印'not found'消息? else 子句对我来说似乎是多余的。平等的检验也是多余的;如果该值既不大于也不小于该值,则它必须等于它。

标签: c algorithm binary-search cs50


【解决方案1】:

在二分搜索中有很多小事情你必须做对:处理 length=0 的情况,确保你测试的位置总是有效的,确保你不会溢出(即,`(low +high)/2' 不是最好的写法),确保新的测试位置总是与前一个不同,等等。

在做了一百万次之后,我写的每一个二分搜索现在都是这样完成的:

bool search(int array[], int length, int valueToFind)
{
    int pos = 0;
    int limit = length;
    while(pos < limit)
    {
        int testpos = pos + ((limit - pos) >> 1);

        if (array[testpos] < valueToFind)
            pos = testpos + 1;
        else
            limit = testpos;
    }
    return (pos < length && array[pos] == valueToFind);
}

请注意,我们每次迭代只需进行一次比较,这比其他答案中的搜索要快。我们不是在循环内进行相等性测试,而是可靠地找到要查找的元素所属的位置,每次迭代只使用一次比较,然后在最后测试看看我们想要的元素是否在那里。

我们计算testpos 的方式确保了pos &lt;= testpos &lt; limit,即使长度是可能的最大整数值,它也可以工作。

这种形式还可以很容易地读取您想要查看的不变量,而无需考虑像high&lt;low 这样的奇怪边界条件。当您退出循环时,pos==limit 这样您就不必担心使用错误等等。

这个循环中的条件也很容易适应不同目的的二进制搜索,例如“找到插入 x 的位置,确保它在数组中已经存在的所有 xs 之后”,“找到 first x in the array”,“找到 last x in the array”等。

【讨论】:

  • 非常好的算法,pos 是最小的索引,即使在重复的情况下也是如此。这个答案值得更多的支持和验证。
【解决方案2】:

在我看来 for 循环只会运行 1 次

for(int i = 0, xt = i + 1; i < xt; i++) {}

为了使您的二分搜索算法正常​​工作,您的整数列表也需要进行排序。我不确定 GetInt() 究竟做了什么,但如果它返回一个随机值,那也会导致搜索失败。

【讨论】:

  • GetInt() 是来自CS50 库的函数。它从标准输入中读取一个整数。
【解决方案3】:
  • 正如其他人所提到的,您在 for 循环中的条件错误。

  • 还有一个问题是

        if (value > values[mid])
        {
            en = mid;
            mid = (en+ex)/2;
        }
    
  • 在这里,当 (value &gt; values[mid]) 您将 mid 分配给 en 但必须分配 mid+1 因为您必须在之后搜索元素 mid

  • 同样,如果(value &lt; values[mid])ex必须分配mid-1 因为你必须搜索元素直到索引 1 小于 mid


二分搜索的更好实现如下:

注意:我分别用lowhigh代替enex

int mid; //no need to initialize as mid is initialized at the start of each iteration
int low = 0; //instead of en
int high = n-1; //instead of ex

while( low <= high )
{
    mid = low + ((high - low) / 2);; //updating mid value

    if (value > values[mid])
    {
        low = mid+1; //updating low
    }

    else if (value < values[mid])
    {
        high = mid-1; //updating high
    }

    else // if (value == values[mid])
    {
        printf("found"); //if found print 'found'
        break;
    }
}

if(low>high)
    printf("not found\n");

进行上述更改,您的代码将是:

#include "cs50.h"
#include <stdio.h>

int main(void)
{
    int n = GetInt();
    int value = GetInt();

    if (n <= 0) 
    {
        //handle the error or exit
    }

    int values[n];

    for (int i=0;i<n;i++)
    {
        printf("Put in number %i \n",i+1);
        values[i]=GetInt();
    }

    int mid;
    int low = 0;
    int high = n-1;

    while( low <= high )
    {
        mid = low + ((high - low) / 2);

        if (value > values[mid])
        {
            low = mid+1;
        }
        else if (value < values[mid])
        {
            high = mid-1;
        }
        else 
        {
            printf("found");
            break;
        }
    }
    if(low>high)
        printf("not found\n");

}

样本输入:

5 //n
5 //value
1 2 3 4 5 //array elements

样本输出:

Put in number 1 
Put in number 2 
Put in number 3 
Put in number 4 
Put in number 5 
found

进一步阅读,请阅读:click

【讨论】:

  • @Vinz.R 如果您有任何疑问,请随时提问 :)
  • int values[n]; 具有未定义的行为,如果 n &lt; 0, mid = (low + high)/2; 在算术溢出的情况下具有潜在的未定义行为。最后一个测试if (value == values[mid]) 是多余的。
  • 感谢您指出@chqrlie,我尝试在我的帖子中进行更改。如果我有任何问题,请告诉我。
  • 对我来说看起来不错。紫外线。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-31
  • 1970-01-01
  • 2020-10-06
  • 2010-10-07
  • 1970-01-01
  • 2020-07-25
相关资源
最近更新 更多