注意,以下用mid=lo+((hi-lo)>>1)是为了防止溢出。此处的小括号不是多余的,和运算符的优先级有关系。
题一、二分搜索,如果存在,返回其下标值,如果不存在,返回-1
版本1:lo表示最低位置,hi表示最高位置。
int binarySearch(int*A,int n,int value)
{
int lo=0,hi=n-1;
while(lo<=hi)
{
int mid=lo+((hi-lo)>>1);
if(A[mid]>value)
hi=mid-1;
elseif(A[mid]<value)
lo=mid+1;
else
return mid;
}
return -1;
}这段程序中,搜索的关键字总是在[lo,hi]即左右都是闭区间,因此,最后终止的搜索条件是lo<hi,即lo<=hi时就一直执行while循环。在lo<hi 时,[lo,hi]已经没有元素了,循环终止。
版本二:lo表示最低位置,hi表示最高位置+1,即hi的位置的数不能取。
int binarySearch(int*A,int n,int value)
{
int lo=0,hi=n;
while(lo<hi)
{
int mid=lo+((hi-lo)>>1);
if(A[mid]>value)
hi=mid;
elseif(A[mid]<value)
lo=mid+1;
else
return mid;
}
return -1;
}
这段程序中,搜索的关键字总是在[lo,hi)即左闭右开区间,因此,最后终止的搜索条件是lo=hi,即lo<hi时就一直执行while循环。在lo=hi时,[lo,hi)已经没有元素了,循环终止。
题二、如果存在多个目标数,则返回其第一次出现的下标值
对应的题一的两个版本的代码如下所示:
int binarySearch(int*A,int n,intvalue)
{
intlo=0,hi=n-1;
intfirst=-1;
while(lo<=hi)
{
intmid=lo+((hi-lo)>>1);
if(A[mid]>value)
hi=mid-1;
else if(A[mid]<value)
lo=mid+1;
else
{
first=mid;
hi=mid-1;
}
}
return first;
}
int binarySearch(int *A,int n,int value)
{
int lo=0,hi=n;
int first=-1;
while(lo<hi)
{
int mid=lo+((hi-lo)>>1);
if(A[mid]>value)
hi=mid;
else if(A[mid]<value)
lo=mid+1;
else
{
first=mid;
hi=mid;
}
}
return first;
}
题三、如果要找的值有多个,则返回第一个所在的下标,如果不存在,则返回第一个比它大的值。
同样对应题一的两个版本的代码如下所示:
int binarySearch(int*A,int n,intvalue)
{
intlo=0,hi=n-1;
while(lo<=hi)
{
in tmid=lo+((hi-lo)>>1);
if(A[mid]>=value)//hi+1中始终存储的是>=value的值
hi=mid-1;//所有>=value的值下标必然>=hi+1
else
lo=mid+1;
}
return hi+1;//return lo;结果一样
}
分析如下:当只剩下最后一个元素时,此时lo==hi,若这个元素last==value则hi=hi-1,此时hi指向的是前一个元素,因此需要将hi+1返回。若这个元素last!=value,那么lo=lo+1,而hi不变,此时hi所指向的必然是最后一个比value小的元素,因此返回hi+1。如果用return lo来代替,分析结果类似。
int binarySearch(int*A,int n,intvalue)
{
intlo=0,hi=n;
while(lo<hi)
{
intmid=lo+((hi-lo)>>1);
if(A[mid]>=value)//hi中始终存储的是>=value的值
hi=mid;//所有>=value的值下标必然>=hi
else
lo=mid+1;
}
return hi;
}//必然会终止,不会出现[lo,lo+1)的死循环,把A[lo]>=value和A[lo]<value带入循环即可得出此结论
如屏蔽掉的解释所说,所有>=value的值的下标必然>=hi,因此直接返回hi即可。
注:题三的解法可以用于题二的解中,只需要判断最后的返回值是否是value即可,如果是,则返回,如果不是则返回-1即可和二等价,下面只给出第二个版本对应的代码,第一个类似。
int binarySearch(int*A,int n,intvalue)
{
intlo=0,hi=n;
while(lo<hi)
{
intmid=lo+((hi-lo)>>1);
if(A[mid]>=value)
hi=mid;
else
lo=mid+1;
}
if(hi==n|| A[hi]!=value) //注意此处边界值的判断
return-1;
return hi;
}
题四、如果要找的值有多个,则返回最后一个所在的下标,如果不存在,则返回第一个比它小的值。
对应题一的两个版本的代码如下所示:注意此处有所不同,题一的第二个版本中区间是[lo,hi),此处是 (lo,hi]。
int binarySearch(int*A,int n,int value)
{
int lo=0,hi=n-1;
while(lo<=hi)
{
intmid=lo+((hi-lo)>>1);
if(A[mid]>value)
hi=mid-1;
else
lo=mid+1;
}
return lo-1;//return hi;结果一样
}
int binarySearch(int*A,int n,intvalue)
{
intlo=-1,hi=n-1;
while(lo+1<hi) //和上面的程序不一样,这里不能保证循环终止,可能会出现[lo,lo+1)的死循环,因此在此处判断用lo+1<hi,如果终止时lo+1==hi,则用下面的if语句判断A[hi]的值。
{
intmid=lo+((hi-lo)>>1);
if(A[mid]>value)
hi=mid-1;
else
lo=mid;
}
if(lo+1==hi&& A[hi]<=target)
returnhi;
returnlo;
}
由代码复杂度可以看出,还是用比较常规的[lo,hi]来解题比较方便。
分析和题3类似。参考网站:http://www.cppblog.com/converse/archive/2009/10/05/97905.html
和http://blog.csdn.net/sunmenggmail/article/details/7540970
参考书:《编程珠玑》第四章,第五章