各种缺陷的二分法算法

二分法是思想精髓就是如果左下标和右下标得到的中间下标所在的值等于所要查找的值,算法结束;如果中间的值小于目标值,则说明目标值可能在中间值和右下标所在的区间内
,就将中间下标当成左下标,继续搜索。如果中间值大于目标值,则把中间下标当成右下标,继续搜索。

这是近期在网上用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)
{
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) r=mid-1;
else return mid; } return -1; }

  • 本文作者: 帐前卒
  • 本文链接: http://chillyc.info/2007/1916972/
  • 版权声明: 本博客所有文章除特别声明外,只能复制超链接地址,且必须注明出处!