二进制搜索实现(主要是代码结构问题,又称新手问题)

时间:2018-07-24 22:24:35

标签: c binary-search-tree

我刚刚学习了C的第一步(我以前用python编写代码),因此我在语法和二进制搜索实现方面都面临着巨大的困难。 (我是编程新手。)

这是我的代码:

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

#define size 1000

int binary_search(int array[size], int givenNumber) {
  do {
    int start = 0;
    int end = size;
    int middle = start + end / 2;
    int left = size - 1;
    int right = size + 1;

    if (array[middle] == givenNumber) {
      printf("location: %d, number: %i", middle, givenNumber);
      return middle;
    } else if (array[left] < givenNumber) {

      int start = 0;
      int end = array[left];
      int middle = start + end / 2;
      return middle;
    } else if (array[right] > middle) {

      int start = array[right];
      int end = size;
      int middle = start + end / 2;
      return middle;
    } else {
      return -1;
      break;
    }
  }
  } while (int middle != givenNumber)

int main() {
  int sorted_array[size];
  for (int i = 0; i < size; i++) {
    sorted_array[i] = i;
  }
  return binary_search(sorted_array, 349);
}

我的问题是:

1-编译时,错误是类似于“在未定义块”中”的情况下发生的”

我不知道为什么值没有从do块传递到while块。请注意,我在每个if / else-if块中添加了“ return middle”,因为我认为它可能有帮助。

2-我什至不确定我的这种二进制搜索的自己实现是否正确。我已经看过如何实现它,但是我发现它几乎无法阅读语法,这只是给个提示。

更新: 我已经根据用户在下面的答案中给出的注释重构了整个代码,而且,我的算法正在运行,现在可以在任何给定的数组中找到任何数字,但是我无法找出一种方法来判断是否数组没有那个givenNumber,因为它最终会卡住。

这是输入/输出: int array[size] = {1,2,3,4,5,6,8,9,10,11,14,24,53,100};

功能:binary_search(array, 24);

输出:Location: 11, Number: 24

这很好,但是,如果我输入了数组中不存在的数字,则循环会不断搜索(陷入递归状态)

here's the updated code:
#include <stdio.h>
#include <stdlib.h>
#define size 14
int start = 0;
int end = size -1;
int middle;
int left;
int right;
int binary_search(int array[size], int givenValue)
{

    middle = (start + end) / 2;
    printf("\nstart: %d \nend: %d \nmiddle: %d \n\n",start, end, middle);

    do
    {
        if (start > end)
        {
            printf("item isn't found");
            break;
            return -1;
        }
        middle = (start+end)/2;
        left =  middle -1;
        right = middle +1;
        if (array[middle] ==  givenValue)
        {
            printf("Location: %d, Number: %d", middle, givenValue);
            return middle;
            break;
        }
        if(array[middle] > givenValue)
        {
            end = right;
            return binary_search(array, givenValue);
        }
        if(array[middle] < givenValue)
        {
            start = left;
            return binary_search(array, givenValue);
        }

    }
    while (start <= end);
}

int main(void)
{
    int array[size] = {1,2,3,4,5,6,8,9,10,11,14,24,53,100};
    return binary_search(array, 24);
}

我的条件是,如果start大于end,则该项目不存在,并且根本不起作用,因为left/right一直停留在相同的值(运行代码以了解我的意思)

如何解决此问题?

3 个答案:

答案 0 :(得分:1)

有很多问题:

  1. 循环不会进行迭代。 if/else的所有部分都有一个return
  2. 大量变量的“阴影”量
  3. 大小是不变,所以左/右总是 设置为同一内容
  4. leftright应该分别从0size - 1开始
  5. 对变量的使用不一致(例如start既是数组索引又是数组 value
  6. 该函数比实际需要的更为复杂,并且具有一些无关的变量
  7. 您的二进制搜索算法令人怀疑

首先,我建议删除所有遮盖外部作用域的变量的“子作用域”变量声明(即,将所有变量置于最外部作用域)。

执行此操作,直到您对这些声明更满意为止。详细了解以下内容之间的区别:

  1. 变量声明:int x;
  2. 带有初始值设定项的变量声明:int x = 5;
  3. 变量分配:x = 5;

我已经注释了您的原始功能,创建了测试/诊断功能,并创建了通过诊断测试的重构功能:

#include <stdio.h>

#define size 1000

int
binary_fixed(int *array, int givenNumber)
{
    int left;
    int right;
    int middle;
    int curval;
    int retindex = -1;

    left = 0;
    right = size - 1;

    while (left <= right) {
        middle = (left + right) / 2;
        curval = array[middle];

        if (curval == givenNumber) {
            retindex = middle;
            break;
        }

        if (curval > givenNumber)
            right = middle - 1;
        else
            left = middle + 1;
    }

    return retindex;
}

int
binary_search(int *array, int givenNumber)
{
    int middle;

    // NOTE/BUG: this does _not_ iterate
    // NOTE/BUG: _massive_ amount of "shadowing" of variables
    // NOTE/BUG: size is _invariant_ so left/right are _always_ set to the
    // same thing
    // NOTE/BUG: left and right should start at 0 and size - 1 respectively
    // NOTE/BUG: variables are used _inconsistently_ (e.g. start is both
    // an array index and an array _value_)
    do {
        int start = 0;
        int end = size;
        int middle = start + end / 2;
        int left = size - 1;
        int right = size + 1;

        if (array[middle] == givenNumber) {
            printf("location: %d, number: %i\n", middle, givenNumber);
            return middle;
        }
        else if (array[left] < givenNumber) {

            int start = 0;
            int end = array[left];
            int middle = start + end / 2;

            return middle;
        }
        else if (array[right] > middle) {

            int start = array[right];
            int end = size;
            int middle = start + end / 2;

            return middle;
        }
        else {
            return -1;
        }
    } while (middle != givenNumber);

    printf("BADRETURN givenNumber=%d\n", givenNumber);
}

int sorted_array[size];

void
test(const char *who,int (*fnc)(int *,int))
{
    int i;
    int r;

    for (i = 0; i < size; i++) {
        r = fnc(sorted_array, i);

        if (r != i) {
            printf("ERROR -- EXPECTED: %d ACTUAL: %d (from %s)\n", i, r, who);
            // break;
        }
    }
}

int
main()
{

    for (int i = 0; i < size; i++) {
        sorted_array[i] = i;
    }

    //test("ORIGINAL",binary_search);
    test("FIXED",binary_fixed);

    return 0;
}

更新:

由于您来自python,因此以下几点可能有助于您的理解。

数组作为 pointers 传递给函数,因此在函数参数列表中,int *array是等效的。进行int array[size]是一项高级技术。现在避免它。在C语言中,您无法像在python中那样执行array.count。因此,暂时将计数作为附加参数传递。

指针是python所没有的,因此您必须学习如何使用它们。一旦掌握了这些技巧,它们就会非常强大,并且可以使代码运行得很快。

在C语言(以及大多数语言 python [和php]除外)中,默认范围是 global 。与python相反。在python中,除非您指定global x,否则x是函数专有的。在其他语言中,要使x在函数本地,必须在函数范围内声明它。

在C语言中,必须在所有位置声明所有变量 :全局范围,函数范围[或函数内的块范围]或作为争论。并且,每个声明必须指定一个显式类型。没有与JavaScript的var x声明等效的声明。之后,x可以是字符串或值,具体取决于您将其设置为什么(例如x = 23x = "abc"

在C语言中,x必须具有以下类型:int x;double x;int *x;char *x


这是带注释的修改后代码:

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

// NOTE: this is hardwired
#define size 14

// NOTE: this is _global_ scope -- while not absolutely wrong, using function
// scope below is faster/better and provides better isolation -- this doesn't
// scale as well
int start = 0;
int end = size - 1;
int middle;
int left;
int right;

// NOTE: in C, using "int *array" is equivalent [and in many cases preferred]
// NOTE: the array count should be passed in as a separate argument
int
binary_search(int array[size], int givenValue)
{
    // NOTE: this is _function_ scope

    // NOTE: this calc of middle is extraneous because it is recalculated
    // below
    middle = (start + end) / 2;
    printf("\nstart: %d \nend: %d \nmiddle: %d \n\n", start, end, middle);

    // NOTE/BUG: this function combines _both_ a loop implementation and a
    // recursive implementation -- we have to pick one or the other as trying
    // to do both messes things us -- the recursion is broken [see below]

    // NOTE: this loop checks start vs end _twice_ in the loop -- it only
    // needs to check in one place -- convert this to "while (start <= end) {"
    // instead of a "do {...} while (whatever);" loop [which is usually not
    // as good]
    do {
        if (start > end) {
            printf("item isn't found");
            break;

            // NOTE/BUG: this return will _never_ be executed because we
            // break out of the loop and what is worse we'll return an
            // unknown/undefined value because
            return -1;
        }

        middle = (start + end) / 2;

        // NOTE/BUG: these are reversed
        left = middle - 1;
        right = middle + 1;

        if (array[middle] == givenValue) {
            printf("Location: %d, Number: %d", middle, givenValue);
            return middle;
            break;
        }

        // NOTE/BUG: these recursive calls do nothing because they do _not_
        // further limit the scope and cause infinite recursion -- to make
        // a recursive version work, start/end would need to be arguments:
        //   return binary_search(array,givenValue,start,end)
        // and we shouldn't loop -- the recursive calls just aren't needed

        // NOTE/BUG: the tests here are broken -- they are the reverse of
        // the correct ones

        if (array[middle] > givenValue) {
            end = right;
            return binary_search(array, givenValue);
        }

        if (array[middle] < givenValue) {
            start = left;
            return binary_search(array, givenValue);
        }

        // NOTE/BUG: this extra test is extraneous and would be done too late
        // if the array size was zero -- a case that isn't handled here
    } while (start <= end);

    // NOTE/BUG: when we break out of the loop, we need to return _some_ value
    // here -- this would be flagged by the compiler using the -Wall option
}

int
main(void)
{
    int array[size] = { 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 14, 24, 53, 100 };

    // NOTE/BUG: the return value from main can only handle numbers 0-255
    // better to print the return value
    return binary_search(array, 24);
}

这是您修改后的代码的有效版本。

循环现在是一个简单的while。该函数采用 separate 计数参数。它循环而不是递归。反向if逻辑已得到修复。再次,添加了诊断测试。

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

int
binary_search(int *array, int size, int givenValue)
{
    // NOTE: this is _function_ scope
    int start = 0;
    int end = size - 1;
    int middle;
    int left;
    int right;
    int match_index;

    // assume failure
    match_index = -1;

    // NOTE: this calc of middle is extraneous because it is recalculated
    // below
    middle = (start + end) / 2;
    printf("\nstart: %d \nend: %d \nmiddle: %d \n\n", start, end, middle);

    while (start <= end) {
        middle = (start + end) / 2;
        left = middle - 1;
        right = middle + 1;

        if (array[middle] == givenValue) {
            printf("Location: %d, Number: %d\n", middle, givenValue);
            match_index = middle;
            break;
        }

        if (array[middle] > givenValue) {
            end = left;
        }

        if (array[middle] < givenValue) {
            start = right;
        }
    }

    if (match_index < 0)
        printf("match not found -- givenValue=%d\n",givenValue);

    return match_index;
}

int
main(void)
{
    int array[] = { 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 14, 24, 53, 100 };
    int count = sizeof(array) / sizeof(array[0]);
    int curidx;
    int valwant;
    int match;

    printf("%d\n",binary_search(array, count, 24));

    // run diagnostic on all values
    for (curidx = 0;  curidx < count;  ++curidx) {
        // get value to search for
        valwant = array[curidx];

        match = binary_search(array,count,valwant);

        if (match != curidx) {
            printf("fault: curidx=%d valwant=%d match=%d\n",
                curidx,valwant,match);
        }
    }

    // test lower range failure
    valwant = array[0] - 1;
    match = binary_search(array,count,valwant);
    if (match >= 0)
        printf("fault: valwant=%d match=%d\n",valwant,match);

    // test upper range failure
    valwant = array[count - 1] + 1;
    match = binary_search(array,count,valwant);
    if (match >= 0)
        printf("fault: valwant=%d match=%d\n",valwant,match);

    return 0;
}

答案 1 :(得分:0)

典型的二进制搜索实现将循环或递归直到找到数字。天真的递归代码是这样的:

#include <stdio.h>

#define size 1000

int binary_search(int array[size], int givenNumber, int start, int end) {
    int middle;
    middle = (start + end) / 2;

    if (start > end)
        return -1;

    if (array[middle] == givenNumber) {
        printf("location: %d, number: %i", middle, givenNumber);
        return middle;
    } else if (array[middle] < givenNumber) {
        return binary_search(array, givenNumber, middle + 1, end);
    } else { // if (array[middle] > givenNumber)
        return binary_search(array, givenNumber, start, middle - 1);
    }
}

int main() {
    int sorted_array[size];
    for (int i = 0; i < size; i++) {
                            sorted_array[i] = i * 2;
    }
    if (binary_search(sorted_array, 349, 0, size - 1) < 0) {
        printf("value not found\n");
    }
    if (binary_search(sorted_array, 34, 0, size - 1) < 0) {
        printf("value not found\n");
    }
}

在递归中,我们每次都根据已排序的数组和givenNumber中提供的输入,使用新的开始和结束范围来调用binary_search。

答案 2 :(得分:-2)

  1. 您不能在while语句中声明变量。将其移至函数顶部。
  2. 您的代码中的花括号位置错误。

尝试一下:

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

#define size 1000

int binary_search(int array[size], int givenNumber) {
  int middle;
  do {
    int start = 0;
    int end = size;
    int middle = start + end / 2;
    int left = size - 1;
    int right = size + 1;

    if (array[middle] == givenNumber) {
      printf("location: %d, number: %i", middle, givenNumber);
      return middle;
    } else if (array[left] < givenNumber) {

      int start = 0;
      int end = array[left];
      int middle = start + end / 2;
      return middle;
    } else if (array[right] > middle) {

      int start = array[right];
      int end = size;
      int middle = start + end / 2;
      return middle;
    } else {
      return -1;
    }
  } while (middle != givenNumber);
}

int main() {
  int sorted_array[size];
  for (int i = 0; i < size; i++) {
    sorted_array[i] = i;
  }
  return binary_search(sorted_array, 349);
}