自己也曾写过二分查找算法,可是认识却有点模糊,或者说对算法的流程理解的并不是很透彻。《编程珠玑》里提到用循环不变式的三个性质来检验算法的正确性,即:
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); }