C中数据结构和算法分析中的二进制搜索

时间:2017-03-23 09:06:20

标签: c algorithm binary-search

在第2章(2.4.4)中解决了Binary_Search时,作者提到了 "

  

请注意,变量不能声明为无符号(为什么?)。在个案中   如果无符号限定符有问题,我们将不会使用它。如   例如,如果unsigned限定符不依赖于数组   从零开始,我们将丢弃它。我们不会用它。作为一个   例如,如果unsigned限定符不依赖于数组   从零开始,我们将丢弃它。我们也会避免使用   作为for循环中的计数器的变量的无符号类型,因为   通常会改变循环计数器的方向   降低和无符号限定符通常适用于   前一种情况。例如,练习2.10中的代码没有   如果我被宣布为未签名的话。   "

代码如下:

int binary_search( input_type a[ ], input_type x, unsigned int n )
{
int low, mid, high; /* Can't be unsigned; why? */
/*1*/ low = 0; high = n - 1;
/*2*/ while( low <= high )
{
/*3*/ mid = (low + high)/2;
/*4*/ if( a[mid] < x )
/*5*/ low = mid + 1;
else
/*6*/ if ( a[mid] < x )
/*7*/ high = mid - 1;
else
/*8*/ return( mid ); /* found */
}
/*9*/ return( NOT_FOUND );
}

问: 我无法理解变量不能被声明为无符号。为什么无符号限定符有问题?无符号限定符如何将循环计数器的方向从增加变为减少?

2 个答案:

答案 0 :(得分:3)

如果mid为0,您希望行high = mid - 1;high设置为-1,这将导致循环停止。

如果变量是无符号的,high将回绕到最大无符号值,这将导致读取超过缓冲区的末尾并可能发生崩溃。

对于倒计时的循环,以下循环永远不会结束:

for (unsigned i = START_VAL; i >= 0; i--) {...}  //WRONG

由于i是无符号的,因此条件i >= 0将始终为真。

答案 1 :(得分:2)

这本书的作者是错的,似乎他是一个弱的程序员。:)

首先,使用类型int作为数组的大小是个坏主意。他应该使用标头size_t中定义的类型ptrdiff_t或至少类型<stddef.h>,因为数组的大小值可能大于类型{{1}的值可以容纳。

考虑到处理数组大小的所有C标准函数(例如字符串函数)将相应的参数定义为类型int

以下是标准函数size_t的声明。

bsearch

这两个参数void *bsearch(const void *key, const void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)); nmemb都有size类型。

问题不在于使用有符号或无符号的int类型作为数组大小的类型。问题是如何实现算法。

例如,他可以按照以下方式实施算法,如演示程序中所示

size_t

程序输出

#include <stdio.h>

size_t binary_search( const int a[], int x, size_t n )
{
    size_t low = 0, high = n;

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

        if ( a[middle] < x )
        {
            low = middle + 1;
        }
        else if ( x < a[middle] )
        {
            high = middle;
        }
        else
        {
            return middle;
        }
    }

    return n;
}   

int main(void) 
{
    int a[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    size_t N = sizeof( a ) / sizeof( *a );

    for ( int i = -1; i <= a[N - 1] + 1; i++ )
    {
        printf( "x = %d: %zu\n", i, binary_search( a, i, N ) );
    }

    return 0;
}

如果您在数组中找不到值,那么该函数将返回等于数组大小的索引。

通常,返回目标元素索引的二进制搜索算法被定义为下限算法。

以下是二进制搜索算法的实现示例,该算法返回目标元素存在或可插入的较低位置。

x = -1: 10
x = 0: 0
x = 1: 1
x = 2: 2
x = 3: 3
x = 4: 4
x = 5: 5
x = 6: 6
x = 7: 7
x = 8: 8
x = 9: 9
x = 10: 10

程序输出可能看起来像

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

size_t binary_search( const int a[], int x, size_t n )
{
    size_t low = 0, high = n;

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

        if ( a[middle] < x )
        {
            low = middle + 1;
        }
        else
        {
            high = middle;
        }
    }

    return high;
}   

int main(void) 
{
    const size_t N = 10;
    int a[N];

    srand( ( unsigned int )time( NULL ) );

    for ( size_t i = 0; i < N; i++ )
    {
        int value = rand() % ( int )N;

        size_t n = binary_search( a, value, i );

        if ( n != i )
        {
            memmove( a + n + 1, a + n, ( i - n ) * sizeof( int ) );
        }

        a[n] = value;

        for ( size_t j = 0; j < i + 1; j++ )
        {
            printf( "%d ", a[j] );
        }
        putchar( '\n' );
    }

    return 0;
}

如您所见,在算法的实现中未使用签名8 1 8 1 5 8 0 1 5 8 0 1 5 5 8 0 0 1 5 5 8 0 0 1 2 5 5 8 0 0 1 2 2 5 5 8 0 0 1 2 2 5 5 8 9 0 0 1 2 2 5 5 5 8 9 类型。

至于其他答案中显示的循环

int

然后它又被错误地写了。在这种情况下,应该使用for (unsigned i = START_VAL; i >= 0; i--) {...} //WRONG 循环而不是for循环,例如

do-while