算法实现回顾1——二分查找
前话:
为什么写这个系列?
算法的精髓除了在于算法的一个设计,更在于算法的一个好的实现。前者可能需求一个好的算法工程师,而后者则需求一个优秀的程序员。
很多时候我们往往只希望去了解一种设计思路,但是对于程序员,一种优良的实现是非常重要的。
实现的细节才决定成败。毕竟程序员面对和输出的都是程序,而不是思路。
引用:
你真的会二分查找吗?http://blog.csdn.net/int64ago/article/details/7425727
二分查找,你真的会吗?http://www.ahathinking.com/archives/179.html
你真的会写二分检索吗?http://blog.chinaunix.net/uid-1844931-id-3337784.html
正文
要说实现,必须从思路开始,一个开始位置start和一个结束位置end。
问题一,我们知道循环结束的结束条件是start要超过end,那么等号怎么办?
现在我们考虑检测的数组只有1个数,我们实现时循环不包含等号,例如:
while(start<end){
//do binary search
}
此时,不进入循环,我们无法判断查找的数和数组中的数哪个大。因此,避免这种情况就是循环条件带等号,循环结构就如下:
while(start<=end){
//do binary search
}
下一步就是定位中间位置:mid=(start+end)/2 or >>1
然后比较 mid 位置的值和查找的值,然后迭代替换start或end,下面需要考虑怎么替换,或者说用哪个值替换?(mid or mid+1 mid-1???)
start=mid Or start=mid+1
我们考虑若采用第一种方式赋值,如果start==end-1,mid则为(start+end)/2==start,start替换成mid后,一直在循环中,函数无法结束。
所以end替换的时候用mid-1,start替换的时候mid+1
问题三是个附加问题:等号需要额外处理吗?
正常的逻辑是等两者相等时返回,但这并不是必须的,考虑:
若等号被包含在 中间值大于查找值 ,那么 区间左移,最终end 指向查找值的左边,start指向查找值。
反之,区间右移,最终 start 指向查找值的右边,end指向查找值。
最后一个问题,返回值是啥?
首先我们考虑跳出循环时,start end 和 mid是什么关系。这里我们先假设 当搜索值和mid的值相等时,函数已返回。
start 和 end 之间的差距 以对半的速度减小 ,最终间隔 1 相等 直到 end-1==start
而mid 的值则不定 ,可能是start也可能是end,而可以肯定的是它是倒数第二步中start和end 的值,而我们不知最后一步中是end左移还是start右移。
因为start和end是有关系的,所以返回哪个都是可以的。
再考虑若我们采用第三问中提及的,不处理等号,这时候根据第三问的解答,相信大家也知道要怎么处理了。
OK,现在看下正常的实现
int bSearch(int data[],int len,int val)
{
if(*data==val)return 0;
if(*data+len-1==val)return len-1;
int s=0,e=len-1,mid=0;
while(s<=e)
{
mid=(s+e)/2;
if(*data+mid==val)return mid;
if(*data+mid>val)e=mid-1;
else s=mid+1;
}
return e;
}
浙公网安备 33010602011771号