【问题标题】:Binary search implementation in CC中的二进制搜索实现
【发布时间】:2017-03-28 18:03:24
【问题描述】:

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

#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);
    }
}

【问题讨论】:

  • 为什么要这样调用函数 binarysearch(array, 8, 15));当数组只有 6 个元素时?
  • 你不能(不应该——不能有意义地)返回 array[0] 仅仅因为数组中有一个元素;它可能不等于您正在寻找的值。您需要返回递归调用找到的值;当调用代码使用未返回的值时,您会调用未定义的行为。
  • @JonathanLeffler 他可以,但他不应该那样做。:)
  • @VladfromMoscow:是的——公平评论;通过修订解决。
  • 另一种方法是使用bsearch

标签: c search binary


【解决方案1】:

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

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

还有这个sn-p

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

不正确。第一个元素不一定等于target。

此声明

    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 + mid, size - mid, target); 不正确:它不是数组的偏移量。您必须存储结果,将其与 -1 进行比较,如果不相等则添加 mid
  • @chqrlie 他不返回职位。他返回了目标。:)
  • 如果找到则返回目标,如果没有则返回 -1 或某个随机值。目前尚不清楚预期的语义是什么。无论如何返回目标都是伪造的,因为 -1 是无法搜索的有效int。您提出的替代 API 没问题,我的更简单。
  • 您好,感谢大家的帮助。弗拉德你解释了为什么你做二进制搜索(数组+中+1,大小-中-1,目标);代替?
  • @Kelvin 假设数组只有一个元素。然后中间等于 0。如果 a[0]
【解决方案2】:

您调用 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 &lt; b

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

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

【讨论】:

  • 这解决了一个问题;它不能解决其他几个问题。
  • @JonathanLeffler:我更新了答案。我相信它现在已经完成,在一般情况下使用更少的比较并且正确处理重复。
  • 代码看起来更好。 IIRC,当有多个匹配条目时,逻辑给出最低索引条目。我对您区分“恒定查找时间”和“O(log N) 时间复杂度”感到有些困惑。我错过了什么?
  • @JonathanLeffler:函数的时间复杂度是对数的,对于给定的 N,它执行完全相同的步数。我将更新我的措辞。
  • @chux:我想过……使用当前的 API,返回 (size_t)-1 会很尴尬。返回指针或布尔值的替代方法更适合非常大的数组。经典的(a + b) / 2值得一提。
【解决方案3】:

您可以使用&lt;stdlib.h&gt; 库提供的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);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多