自己也曾写过二分查找算法,可是认识却有点模糊,或者说对算法的流程理解的并不是很透彻。《编程珠玑》里提到用循环不变式的三个性质来检验算法的正确性,即:
1.初始化:保证循环开始前初始条件是正确的。
2.保持:如果在循环的某一次迭代开始之前它是正确的,那么,在下一次迭代开始之前,它也应该保持正确。
3.终止:循环能够终止,并且可以得到期望的结果。
int BinSearch(int *array, int size, int key) //前置条件是数组已是升序排列
{
if(!array)
return -1;
int left = 0, right = size, mid;
while(left <= right)
{
mid = left + (right - left)/2; //或右移一位,这种写法是为了防止溢出
if(array[mid] < key)
left = mid + 1;
else if(array[mid] > key)
right = mid - 1;
else
return mid;
}
return -1;
}
如果key在数组中可能重复,要求返回key出现的第一个下标
int BinSearchFirst(int *array, int size, int key) //前置条件是数组已是升序排列
{
if(!array)
return -1;
int left = 0, right = size, mid;
while(left < right)
{
mid = left + (right - left)/2; //或右移一位,这种写法是为了防止溢出
if(array[mid] >= key)
right = mid; //循环减少right-mid,如果right==mid(此时构成死循环),有right==left,因此循环终止条件不包括==
else
left = mid + 1; //循环减少mid+1-left,至少减少为1
}
if(array[left] == key) //终止时有left>=right,若left>right,则left上一步有left=mid+1,mid+1>right,退出mid==right,left==right,循环一开始就不会出现
return left; //若left==right,如果array[left]==key,返回left即可
return -1;
}
对于要求返回最后一个等于key的位置x的算法,情况下更复杂一些.当array[mid]==key时,x可能存在于(mid,…,right)中,此时令left=mid,循环减少mid-left。由于mid通过除法运算舍去小数部分后更靠近left,所以mid==left时有left+1==right或left==right,不同于求第一次出现位置时的right==mid只有一种情况,与上例的主要区别也就在这里。因此循环终止条件应设为left<right-1.
int BinSearchLast(int *array, int size, int key)
{
if(!array)
return -1;
int left = 0, right = size, mid;
while(left < right-1)
{
mid = left + (right - left)/2;
if(array[mid] <= key)
left = mid;
else
right = mid - 1;
}
if(array[right] == key)
return right;
else if(array[left] == key)
return left;
else
return -1;
}
递归版本的二分搜索原理与迭代版本相同,需要注意递归的不变式为判断语句,迭代为循环。
//递归版本的二分搜索
int Search(int *array, int left, int right, int key)
{
int mid = left + (right-left)/2;
if(left <= right)
{
if(array[mid] < key)
return Search(array, mid+1, right, key);
else if(array[mid] > key)
return Search(array, left, mid-1, key);
else
return mid;
}
else
return -1;
}
int BinSearch(int *array, int size, int key)
{
if(!array)
return -1;
int left = 0, right = size;
return Search(array, left, right, key);
}