所谓的盲写,就是在百度百科上简单看了一眼二分查找的原理,在纸上画画伪码,一拍脑袋:哦,想清楚了,小菜一碟。然后下笔如有神,语法都不带检查的大笔一挥,得到第一版代码。
public static int doSearch(int[] arry, int target){
// because arry is ASC
if (target < arry[0] || target > arry[arry.length-1]) {
return -1;
}
int low_index = 0;
int high_index = arry.length-1;
int mid_index;
while (low_index < high_index) {
mid_index = low_index + (high_index - low_index)/2;
int mid_val = arry[mid_index];
if (target > mid_val) {
low_index = mid_index;
continue;
}
if (target < mid_val) {
high_index = mid_index ;
continue;
}
if (target == mid_val) {
return mid_index;
}
}
return -1;
}
low_index = mid_index + 1
单元测试一测,一个测试方法一直出不来,代码运算量并不大,果断有死循环。这也给自己提了个醒,测试方法应加上timeout。Debug一下,发现死循环发生在测试下边界值处。原来第一处打脸的地方是在 low_index = mid_index 。
原因分析一下是,当对low_index和high_index之间的区域折半时,得到的一半值mid_index永远会偏向low_index,也就是:恰好有中间整数,得到的就是中间值;如果没有中间整数值,则滑向最接近的低位整数。如果low_index和high_index刚好相差1,mid_index的计算结果一直等于low_index,循环陷在将mid_index的计算结果为low_index然后再赋给low_index中出不来。
因此,low_index = mid_index修改为low_index = mid_index + 1,同时high_index = mid_index - 1。这本身也是折半的原理所在:折半对比,目标大于半数值,从高于折半处截取重新查找(low_index = mid_index + 1);目标小于半数值,从低于折半处截取重新查找(high_index = mid_index - 1);总之不相等则丢弃折半处。
while (low_index < =high_index)
当使用while (low_index < high_index),对于数组长度为5或者6时,索引处1的值查找不出来。原因在于最后一次折半前,高低位重合,跳出循环,而来不及计算mid_index。
最后的写法,通过测试
public static int doSearch(int[] arry, int target){
// because arry is ASC
if (target < arry[0] || target > arry[arry.length-1]) {
return -1;
}
int low_index = 0;
int high_index = arry.length-1;
int mid_index;
while (low_index < =high_index) {
mid_index = low_index + (high_index - low_index)/2;
int mid_val = arry[mid_index];
if (target > mid_val) {
low_index = mid_index + 1;
continue;
}
if (target < mid_val) {
high_index = mid_index - 1;
continue;
}
if (target == mid_val) {
return mid_index;
}
}
return -1;
}