refer to:
https://blog.csdn.net/u011523762/article/details/50878613
用伪代码来表示, 二分查找算法大致是这个样子的:
left = 0, right = n -1
while (left <= right)
mid = (left + right) / 2
case
x[mid] < t: left = mid + 1;
x[mid] = t: p = mid; break;
x[mid] > t: right = mid -1;
return -1;
边界错误造成的问题:
1, miss one item (右边界:R < n or <= n - 1)
二分查找算法的边界,一般来说分两种情况,一种是左闭右开区间,类似于[left, right),一种是左闭右闭区间,类似于[left, right]. 需要注意的是, 循环体外的初始化条件,与循环体内的迭代步骤, 都必须遵守一致的区间规则,也就是说,如果循环体初始化时,是以左闭右开区间为边界的,那么循环体内部的迭代也应该如此.
如果两者不一致,
在循环初始化的时候,初始化right=n,也就是采用的是左闭右开区间,而当满足array[middle] > v的条件是, v如果存在的话应该在[left, middle)区间中,但是这里却把right赋值为middle - 1了,这样,如果恰巧middle-1就是查找的元素,那么就会找不到这个元素.
比如下面就是错误的二分查找算法:
int search_bad(int array[], int n, int v)
{
int left, right, middle;
left = 0, right = n;
while (left < right)
{
middle = (left + right) / 2;
if (array[middle] > v)
{
right = middle - 1;
}
else if (array[middle] < v)
{
left = middle + 1;
}
else
{
return middle;
}
}
return -1;
}
2, 死循环 (左边界:L = mid or mid + 1)
把上例中的L=mid+1改成L=mid那就很可能会死循环。因为使用mid = (L+R)/2这种计算方式的话,当R-L=1时,mid是等于L的。而此时如果恰好执行了L=mid,那就意味着在这次iteration中,L的值没有变化,即搜索范围没有变,于是就死循环了。
正解:
下面给出两个算法, 分别是正确的左闭右闭和左闭右开区间算法,可以与上面的进行比较:
int search2(int array[], int n, int v)
{
int left, right, middle;
left = 0, right = n - 1;
while (left <= right)
{
middle = (left + right) / 2;
if (array[middle] > v)
{
right = middle - 1;
}
else if (array[middle] < v)
{
left = middle + 1;
}
else
{
return middle;
}
}
return -1;
}
int search3(int array[], int n, int v)
{
int left, right, middle;
left = 0, right = n;
while (left < right)
{
middle = (left + right) / 2;
if (array[middle] > v)
{
right = middle;
}
else if (array[middle] < v)
{
left = middle + 1;
}
else
{
return middle;
}
}
return -1;
}