关于二分查找,想必大家都非常熟悉了,正如《编程珠矶》中提到的,这是一个简单的程序,可是要实现一个无bug的版本确并非一件易事,笔者最近看了一下二分搜索,简短得概括一下。
对于不同的需求,我们面临的二分查找就会有不同的版本,就我最近遇到的几个版本好好解释一下:
这是最常见的版本,代码也不是很难。最常见的代码如下:
在数组A[ left, right ]中寻找元素 m。
int binarySearch(int A[], int left, int right, int target) { int mid; if(left > right) { cout << "find range error!" << endl; return -1; } while(left <= right) //注意此处的数据范围的空间是[ left, right ]的闭区间,每次进入循环之后保持循环的循环不变式就可以了 { mid = left + (right-left)/2; //避免溢出 assert( ( A[left] <= A[mid] ) && ( A[mid] <= A[right] )); //确保是有序的数组 if ( A[mid] == target ) return mid; else if( A[mid] > target ) right = mid-1; else left = mid + 1; } cout << target << " not exist in A" << endl; return -1; }
二分查找的递归实现:(虽然大部分时候不适用递归,递归的函数调用开销较大,,但是这里还是实现一个递归的版本。)
int binarySearchRecursion(int A[],int left, int right,int m) { if(left <= right) { int mid = left + (right-left)/2; if(A[mid] == m) return mid; else if(A[mid] > m) { right = mid-1; binarySearchRecursion(A,left,right,m); } else { left = mid+1; binarySearchRecursion(A,left,right,m); } } //此处必须加上else,否则在递归调用返回之后,无论值为多少,最终都会是-1!! else { return -1; } }
int binarySearchStartPos(int A[], int left, int right, int m) { int mid, tmid ; if(left > right) { cout << "find range error !!" << endl; return -1; } while(left <= right) { mid = left + (right-left)/2; if(A[mid] == m) //这里首先保证我一定能够找到这样的元素m,那么既然满足这样的条件,返回值就一定不能为-1. { tmid = mid; //刚开始我想的是,循环不变式是:[left right]之间一定包含要寻找的元素m。 //这样不能保证循环的终止。每次我的left和right 并没有保证下一次循环时的元素个数减少!!! right = mid-1; while(left <= right) { mid = left + (right-left)/2; if(A[mid] == m){right = mid-1;tmid = mid;} else if (A[mid] < m) left = mid + 1; else; } return tmid; } else if(A[mid] > m) right = mid-1; else left = mid+1; } cout << m << "not exist in A " << endl; return -1; }