问题描述:
下面7个算法与本章中的二分搜索算法BinarySearch略有不同。请判断这7个算法的正确性,不正确则给出理由。
具体分析:
template<class Type>
int BinarySearch1(Type a[], const Type& x, int n){
int left = 0;
int right = n-1;
while(left <= right){
int middle = (left + right) / 2;
if(x == a[middle])
return middle;
if(x > a[middle])
left = middle;
else
right = middle;
}
return -1;
}
(1).该算法中left的更新值为middle,如果一开始搜索的就是a[n - 1],即x = a[n - 1]
,那么由于除法的向下取整,会使代码陷入死循环,因此错误。
template<class Type>
int BinarySearch2(Type a[], const Type& x, int n){
int left = 0;
int right = n-1;
while(left < right - 1){
int middle = (left + right) / 2;
if(x < a[middle])
right = middle;
else
left = middle;
}
if(x == a[left])
return left;
else
return -1;
}
(2).这个算法由于循环条件是left < right - 1
,所以并不会出现上述死循环情况,因为当left == right - 1
时循环就结束了,但是该算法将搜索成功的判定交给left变量来做,姑且称之为由left负责判定,也正因为这个循环条件,left永远取不到(n-1)这个值,这意味着当x = a[n - 1]
时,会错误地返回-1,而不是(n-1)。
template<class Type>
int BinarySearch3(Type a[], const Type& x, int n){
int left = 0;
int right = n-1;
while(left + 1 != right){
int middle = (left + right) / 2;
if(x >= a[middle])
left = middle;
else
right = middle;
}
if(x == a[left])
return left;
else
return -1;
}
(3).算法3其实与算法2基本等价,因为循环条件是等价的,left和right两个变量的赋值也是等价的,也是由变量left负责判定,所以当x = a[n - 1]
时,就会错误地返回-1,而不是(n-1)。
template<class Type>
int BinarySearch4(Type a[], const Type& x, int n){
if(n > 0 && x >= a[0]){
int left = 0;
int right = n-1;
while(left < right){
int middle = (left + right) / 2;
if(x < a[middle])
right = middle - 1;
else
left = middle;
}
if(x == a[left])
return left;
}
return -1;
}
(4).算法4在前面的基础上添加了一点输入规范性的检查,还有一个大的改变就是right的更新值变成了middle - 1
,left的更新值则不变,前面提到过由于middle的计算是向下取整,当搜索x = a[n - 1]
时,会让代码陷入死循环,因此错误。
template<class Type>
int BinarySearch5(Type a[], const Type& x, int n){
if(n > 0 && x >= a[0]){
int left = 0;
int right = n-1;
while(left < right){
int middle = (left + right + 1) / 2;
if(x < a[middle])
right = middle - 1;
else
left = middle;
}
if(x == a[left])
return left;
}
return -1;
}
(5).算法5既不会出现前面提到的陷入死循环情况,也不会出现返回值错误的情况,因此正确。
template<class Type>
int BinarySearch6(Type a[], const Type& x, int n){
if(n > 0 && x >= a[0]){
int left = 0;
int right = n-1;
while(left < right){
int middle = (left + right + 1) / 2;
if(x < a[middle])
right = middle - 1;
else
left = middle + 1;
}
if(x == a[left])
return left;
}
return -1;
}
(6).算法6在5的基础上,将middle的计算改为向上取整,并且left的更新值改为middle + 1
,可以看到该算法由left负责判定,如果我们第一次计算出的middle对应的a[middle]
就是我们要搜索的数值,那么该算法会错误地返回-1,因此错误。
template<class Type>
int BinarySearch7(Type a[], const Type& x, int n){
if(n > 0 && x >= a[0]){
int left = 0;
int right = n-1;
while(left < right){
int middle = (left + right + 1) / 2;
if(x < a[middle])
right = middle;
else
left = middle;
}
if(x == a[left])
return left;
}
return -1;
}
(7).算法7与6的区别是将left和right两个变量的更新值都改为了middle,这种方式与算法1很相似,当我们要搜索的值x = a[0]时,该算法也同样会陷入死循环。
粗略地总结以下几点规律:
1.当循环结束条件为left = right
时可能会出现死循环的情况。
2.midddle的计算为向上取整时,right的更新值不能取middle;middle的计算为向下取整时,left的更新值不能取middle。否则可能会出现死循环。
3.负责判定的那个变量(left或者right)的更新值只能取middle,否则可能会出现返回值错误的情况。