繁体   English   中英

用C进行二进制搜索

[英]Binary search implementation in C

第一次在这里发布。 我最近实现了二进制搜索,但有时我的输出将返回一个巨大的负数。 现在我的第一个想法是,我正在打印一个数字,我的指针指向一个随机的内存位置。 有人可以帮助我解决逻辑问题,以及如何改善我的代码吗?

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

int binarysearch(int *array, int size, int target);

int main() {
    int array[] = { 1, 2, 3, 4, 5, 6 };
    printf("%d\n", binarysearch(array, 8, 15));
    return 0;
}

int binarysearch(int *array, int size, int target) {
    int mid;    
    mid = size / 2;

    if (size < 1) {
        return -1;
    }
    if (size == 1) {
        return array[0];
    }
    if (target == array[mid]) {
        return target;
    } else
    if (target < array[mid]) {
        binarysearch(array, mid, target);
    } else{
        binarysearch(array + mid, size - mid, target);
    }
}

对于初学者,您可以在只有6个元素的数组中使用无效数量的元素来调用该函数。

int array[] = { 1, 2, 3, 4, 5, 6 };
printf("%d\n", binarysearch(array, 8, 15));
                                  ^^^

也是这个片段

if (size == 1) {
    return array[0];
}

是不正确的。 第一个元素不必等于目标。

这个说法

    binarysearch(array + mid, size - mid, target);

必须写成

    binarysearch(array + mid + 1, size - mid - 1, target);

最后,该函数具有未定义的行为,因为在这些情况下,它不返回任何内容

if (target < array[mid]) {
    binarysearch(array, mid, target);
} else{
    binarysearch(array + mid, size - mid, target);
}

你需要写

if (target < array[mid]) {
    return binarysearch(array, mid, target);
} else{
    return binarysearch(array + mid, size - mid, target);
}

关于编程风格的两个词。 最好将函数命名为binary_searchbinarySearch或最后将BinarySearch binarysearch

通常,这不是功能的良好设计。 想象一下,数组中有一个值为-1的元素。 您将如何确定此元素是存在于数组中还是不存在?

通常,此类函数会在找到目标元素的情况下返回指向目标元素的指针,否则返回NULL指针。

这是一个演示程序,显示了如何实现此方法。

#include <stdio.h>

int * binary_search( const int *a, size_t n, int target ) 
{
    if ( n == 0 ) return NULL;

    size_t middle = n / 2;

    if ( a[middle] < target )
    {
        return binary_search( a + middle + 1, n - middle - 1, target );
    }
    else if ( target < a[middle] )
    {
        return binary_search( a, middle, target );
    }

    return a + middle;
}

int main(void) 
{
    int array[] = { 1, 2, 3, 4, 5, 6 };
    const size_t N = sizeof( array ) / sizeof( *array );

    for ( int i = 0; i < 8; i++ )
    {
        int *target = binary_search( array, N, i  );
        if ( target )
        {
            printf( "%d is found at position %d\n",  *target, ( int )(target - array ) );
        }
        else
        {
            printf( "%d is not found\n", i );
        }
    }

    return 0;
}

程序输出为

0 is not found
1 is found at position 0
2 is found at position 1
3 is found at position 2
4 is found at position 3
5 is found at position 4
6 is found at position 5
7 is not found

顺便说一下,根据C标准函数,没有参数的main应该声明为

int main( void )

您调用binarysearch(array, 8, 15))但是您的数组只有6个条目。

以下是自动计算适当大小的方法:

int main(void) {
    int array[] = { 1, 2, 3, 4, 5, 6 };
    printf("%d\n", binarysearch(array, sizeof(array) / sizeof(array[0]), 15));
    return 0;
}

请注意,您的函数binarysearch也有问题:

  1. 返回数组项是伪造的,如果目标小于第一个条目,您将返回什么? -1不一定小于第一项。

  2. 您应该将索引与条目(如果找到)一起返回到数组中,如果未找到则返回-1

  3. 递归时,不要从这些递归调用中返回值:应该在启用警告的情况下进行编译(例如: gcc -Wall -W ),并查看编译器产生的所有有用的诊断消息。

这是修改后的版本:

int binarysearch(const int *array, int size, int target) {

    int a, b;

    for (a = 0, b = size; a < b;) {
        int mid = a + (b - a) / 2;
        if (target <= array[mid]) {
            b = mid;
        } else {
            a = mid + 1;
        }
    }
    // a is the offset where target is or should be inserted
    if (a < size && target == array[a])
        return a;
    else
        return -1;
}

笔记:

  • 计算mid = (a + b) / 2; 对于大尺寸可能是不正确的,因为可能存在算术溢出。 mid = a + (b - a) / 2; 因为a < b所以没有这个问题。

  • 时间复杂度为O(Log N) ,并且对于给定的size ,该函数对所有目标值执行相同数量的步骤。

  • 如果数组包含等于目标的多个相同值,则binarysearch返回的索引是具有最低索引的匹配条目的索引。

通过使用<stdlib.h>库提供的bsearch函数,可以使此问题更容易解决。

像这样:

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

int cmpfunc(const void * a, const void * b);

int
main(void) {
    int array[] = {1, 2, 3, 4, 5, 6};
    size_t n = sizeof(array)/sizeof(*array);
    int *item;
    int key = 15;

    item = bsearch(&key, array, n, sizeof(*array), cmpfunc);

    if (item != NULL) {
        printf("Found item  = %d\n", *item);
    } else {
        printf("Item = %d could not be found\n", key);
    }

    return 0;
}

int 
cmpfunc(const void * a, const void * b) {
   return (*(int*)a > *(int*)b) - (*(int*)a < *(int*)b);
}

如果您不想使用bsearch ,那么此方法也可以:

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

#define BSFOUND 1
#define BS_NOT_FOUND 0

int cmpfunc(const void * a, const void * b);
int binary_search(int A[], int lo, int hi, int *key, int *locn);

int
main(void) {
    int array[] = {1, 2, 3, 4, 5, 6};
    size_t n = sizeof(array)/sizeof(*array);
    int key = 4, locn;

    if ((binary_search(array, 0, n, &key, &locn)) == BSFOUND) {
        printf("Found item = %d\n", array[locn]);
    } else {
        printf("Item = %d cound not be found\n", key);
    }

    return 0;
}

int
binary_search(int A[], int lo, int hi, int *key, int *locn) {
   int mid, outcome;

   if (lo >= hi) {
      return BS_NOT_FOUND;
   }

   mid = lo + (hi - lo) / 2;
   if ((outcome = cmpfunc(key, A+mid)) < 0) {
      return binary_search(A, lo, mid, key, locn);
   } else if(outcome > 0) {
      return binary_search(A, mid+1, hi, key, locn);
   } else {
      *locn = mid;
      return BSFOUND;
   }
}

int 
cmpfunc(const void * a, const void * b) {
   return (*(int*)a > *(int*)b) - (*(int*)a < *(int*)b);
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM