这是个自以为完全理解能上天,却敲码30秒debug半小时的悲惨故事(我果然是蒟蒻)
首先贴出自我感觉良好,但有bug的代码:
1 osition BinarySearch( List L, ElementType X ){ 2 int left,right,mid; 3 left=1; 4 right=L->Last; 5 mid=(left+right)/2; 6 while(left!=right){ 7 if(X==L->Data[mid]){ 8 return mid; 9 } 10 else if(X < L->Data[mid]){ 11 right=mid; 12 mid=(left+right)/2; 13 } 14 else if(X > L->Data[mid]){ 15 left=mid; 16 mid=(left+right)/2; 17 } 18 if(L->Data[mid]==X)return mid; 19 else if(X==L->Data[right])return right; 20 } 21 return NotFound; 22 }
此代码有两个问题表象问题:
1、为了找到尾部的情况,强行在循环末尾加了一个判断语句(这种治标不治本的写法,你好意思说自己是学算法的吗......)
2、当x找不到时,返回不了NotFound
其实这两个问题可以归结为同一个问题:没有理解中点的偏向问题!
由于 Java 语言(C、C++、Python 等也一样)的除法是自动向下取整,因此中间位置 mid 会偏向左边界 left,所以 left = mid +1而不是 left = mid。因为只要 left 和 right 不相等,left = mid+1 一定会较原来的 left 右移,这样可以确保范围不断缩小。
下面是最后一次循环的典型情况,目标值为 3,right 指针略过了大于或等于 3 的位置,直到第一个 3 处,此时 mid 由于向下取整,等于 left,经过判断,发现 mid 处的值为 2,比目标值小,因此 left = mid + 1,移动到了右侧的位置上。
此时终止条件达成。
所以正确代码如下:
1 Position BinarySearch( List L, ElementType X ){ 2 int left,right,mid; 3 left=1; 4 right=L->Last; 5 mid=(left+right)/2; 6 while(left!=right){ 7 if(X==L->Data[mid]){ 8 return mid; 9 } 10 else if(X < L->Data[mid]){ 11 right=mid; 12 mid=(left+right)/2; 13 } 14 else if(X > L->Data[mid]){ 15 left=mid+1; 16 mid=(left+right)/2; 17 } 18 if(L->Data[mid]==X)return mid; 19 } 20 return NotFound;
小结:遇到整数除法时,注意商的偏向问题。
参考资料: