算法实现回顾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,那么等号怎么办?
TestCase1:1个数,我们允许循环时没等号,例如:
while(start<end){}
一个数的话直接不查了。OK,=必须是要的。
好,继续,start<=end时我们要做什么呢?二分法,当然是mid取中啦。
mid=(start+end)/2 or >>1 其实编译器会优化的,你以为人家傻?人家尾递归都会。
然后比较 mid位置的值和要求的值的,然后替换start或end,这时候问题又来了,
问题二:怎么替换,或者说用哪个值替换?
我们想,如果中间数大,说明值要在前半部分,end必须改掉,ok,改成mid?
这里是个错,如果start和end间隔,mid是start,用start替换可能会造成死循环。。over
所以end替换的时候用mid-1,start替换的时候mid+1
问题三,等号需要处理吗?
通常是要先判断是不是和中间值相等的,等号就直接返回了,这是很正常的逻辑。
而上边第一个链接,里边是没有=的。
我们这样想,如果发生了相等,但我们没直接返回,而继续寻找,直到start=end=(pos-1 or pos+1)。
按链接1中YES_LEFT方法:原数组 1 2 5,找2.
s=0 e=2 m=1 *mid>=val e=mid-1
s=0 e=0 mid=0 *mid<val s=mid+1 return s=1
找3 s=0 e=2 m=1 *mid<val s=mid+1
s=2 e=2 mid=2 *mid>=val e=mid-1 return s=2
此方法的特点是mid和val比较是>=,返回的是start
说明返回的是不小于val的最小值的位置
YES_RIGHT找2.s=0 e=2 m=1 *mid<=val s=mid+1
s=2 e=2 mid=2 *mid>val e=mid-1 return e=1
若找3 s=0 e=2 m=1 *mid<=val s=mid+1
s=2 e=2 mid=2 *mid>val e=mid-1 return e=1
此方法的特点是mid和val比较是>,返回的是end
说明返回的是不大于val的最大值的位置
跳出时必然s>e准确的说s=e+1,当val值未找到时,s是比val大的,e是比val小的
注意的是下边这种可能会返回-1
很多时候我们要找到不大于val数的个数,这是我们选择下边这种+1即可
问题四,进一步处理一些特殊情况.
特殊情况包括,找的正好是首末位。这种情况就是在循环前作判断。
循环内加等号判断也可以加快找到后退出。
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; }
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。