二分法是思想精髓就是如果左下标和右下标得到的中间下标所在的值等于所要查找的值,算法结束;如果中间的值小于目标值,则说明目标值可能在中间值和右下标所在的区间内,就将中间下标当成左下标,继续搜索。如果中间值大于目标值,则把中间下标当成右下标,继续搜索。
这是近期在网上用google搜索“二分法算法”找到的二分法算法。结果发现各种各样的缺陷。
public int erfen()
//二分法查找
{
int iindex=0; //相当于指针的东西
int istart=0; //
int iend=iarray.length-1;
while(true)
{
icount++;
iindex = (istart+iend)/2;
if(iarray[iindex]<iseek)
{
istart = iindex;
}
else if(iarray[iindex]>iseek)
{
iend = iindex;
}
else
{
break;
}
}
return icount;
}
用一个测试用例就能证明的这个算法错误。
0 1 2 3 4 5查找5.结果你会发现死循环。
很多人认为只要起始值的左值和右值相加为n-1就可以了。其实不然,二分法是10年多的时间才找到了一种正确的算法。如果你开始的左值设为0并且右值设为n-1,程序很大的程度上会错。除非你解决了最后的死循环问题。
下面的算法
left
=
-
1
;
right
=
length;
while
(left
<=
right)

...
{
mid
=
(left
+
right)
/
2
;
if
(a[mid]
>
value)

...
{
//
中间值比目标值大
right
=
mid;
}
else
if
(a[mid]
<
value)
...
{
//
中间值比目标值小
left
=
mid;
}
else

...
{
//
找到,中间值等于目标值
return
left;
}

}
return
-
1
;
可以解决上面的0 1 2 3 4 5 查找5找不到的问题。但是,却在查找不存在的数上出了问题。比如6
下面的代码解决了这个问题:
left
=
-
1
;
right
=
length;
while
(left
+
1
!=
right)
//
注意这句话!!!

...
...
{
mid
=
(left
+
right)
/
2
;
if
(a[mid]
>
value)

...
...
{
//
中间值比目标值大
right
=
mid;
}
else
if
(a[mid]
<
value)
...
...
{
//
中间值比目标值小
left
=
mid;
}
else

...
...
{
//
找到,中间值等于目标值
return
left;
}

}
return
-
1
;
但是上的这段真的解决了所有的问题?如果left + right 超过了整数范围?
改为下面的代码:
int
BinarySearch(
int
value)
...
{

int
left
=
-1
;
int
right
=
length
;
int
mid;
while
(left+1!
=
right)
...
{
mid
=
left
+
(right
-
left)
/
2
;
//
注意这里的写法
if
(values[mid]
==
value)
...
{
return
mid;
}
else
if
(values[mid]
<
value)
...
{
left
=
mid;
}
else

...
{
right
=
mid;
}
}
return
-
1
;
}
这里解决了越界问题。但是这段代码真的没有问题了吗?没有人能保证。除非能用数学证明。
另外还有一种写法:但是必须特判length==0的情况,否则一定会访问越界。
int bsearch(int* a, int l, int r,int k) { int mid = 0; if(l-r == 0) return -1; while(l<=r){ mid = (l+r)/2; if(a[mid]<k) l = mid+1; else if(a[mid]>k) r=mid-1; else return mid; } return -1; }
本文深入探讨了二分法算法的实现细节及常见问题。通过分析不同版本的二分法代码,指出了容易忽视的边界条件处理问题,并提供了一种避免整数溢出的解决方案。
651

被折叠的 条评论
为什么被折叠?



