您的位置:首页 > 编程语言 > C语言/C++

C语言二分查找法实现与细节

2016-05-16 17:42 337 查看
刚学C的时候没过多久就接触到这种算法,总的来说这个算法并不难。时隔好久,今天看了下书,加深了一些理解,谨记此文,用于回顾及梳理知识。

如要求:

编写一个函数,对一个已排序的整数表执行二分查找。函数的输入包括一个指向表头的指针,表中的元素个数,以及待查找的数值。函数的输出是一个指向满足查找要求的元素的指针,当未查找到要求的数值时,输出一个NULL指针。

下图显示大致搜索过程:



假定x是要搜索的元素,k表示要搜索的元素的下标,n是表中的元素个数。任何时候假定lo和hi是不对称边界的两头。也就是说,lo <= k < hi。

如果lo与hi相等,此时可能范围已缩为空,所以可以判定x不在表中。如果lo小于hi,那么可能范围中至少存在一个元素。假定mid为可能范围的中值,然后比较x与表中下标为mid的元素。如果x比该元素小,那么mid就是位于可能范围以外的最小下标,因此可以设置hi = mid。如果x比该元素大,那么mid + 1就是位于新的已缩减的可能范围以内的最小下标,因此可以设置lo = mid + 1。如果x与该元素相等,则完成搜索。

对于假定mid为可能范围的中值。存在这样一个问题:是否可以设置mid = (hi + lo) / 2; 这样设置会不会带来一些问题?

分析:如果hi与lo相隔较远这样做显然没问题,但是如果相隔太近又是怎么样情况?

对于hi等于lo的情况不用考虑,因为已经判断出x的可能范围为空,所以甚至不需要设置mid。而当hi = lo + 2时,hi + lo = lo * 2 + 2,这是个偶数,因此 (hi + lo) / 2 = lo + 1。当hi = lo + 1时,可能范围中的唯一元素就是lo,由于hi + lo恒为正数,(hi + lo) / 2肯定会等于lo,因为在这种情况下整数除法会被截断处理,即(hi + lo) / 2 = (2lo + 1) / 2,结果就为lo。

所以根据以上分析,mid = (hi + lo) / 2; 没有问题。

具体代码如下:

/* 数组实现 */
int *bsearch(int *t, int n, int x)
{
int lo = 0, hi = n;
while (lo < hi)
{
int mid = (lo + hi) / 2;
if (x < t[mid])
hi = mid;
else if (x > t[mid])
lo = mid + 1;
else
return t + mid;
}
return NULL;
}


值得一提的是,int mid = (lo + hi) / 2;可以改写为int mid = (lo + hi) >> 1;这样会提高程序的运行速度,将上面的程序中一些寻址运算替换,在很多机器上下标运算都要比指针运算慢。改写后的程序如下:

/* 指针实现 */
int *bsearch(int *t, int n, int x)
{
int *lo = t, *hi = t + n;
while (lo < hi)
{
/* int *mid = (lo + hi) / 2;  注意!这种写法是非法的,它将两个指针相加 */
int *mid = (lo + (hi - lo)) >> 1;  /* 正确方法 */
if (x < *mid)
hi = mid;
else if (x > *mid)
lo = mid + 1;
else
return mid;
}
return NULL;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: