写好一段正确并且完整的二分查找也不容易
昨天去有道二面,在一个关键词搜索纠错的设计题后,来了一道程序题。是循环数组的二分查找。当时,因为前面扯的时间较长,而面试时间固定在一个小时到一个小时十五分之间,面试官说大致如此就没让我写全。后来自己回到宿舍写了下,发现竟然还是蛮搞的。尤其是刚开始思路没理清楚去做事情的时候,反反复复,甚是烦人。所以写此篇,以作警示。
题目: A是有序数组,B是将A斩断后调换前后顺序的数组(也就是像右循环移动了k个元素)。给出一个元素e,问如何高效的寻找这个e是不是在数组B中。
思路:循环数组+二分查找
解答过程:
1. 循环数组,无非real index和logic index,logic index = (real index – k + len )%len.
2. 如何正确的写一个二分查找
下面的函数是很通用的一个二分查找的过程,但如果把logic index的方法直接用到下面这个函数上会有边界的问题。例如r = 0时候,l=0,那么如果给r-1的时候,就会得到r=(r-1+len)/len = len-1一下子跳到了最右边的位置,所以l<r依旧成立,这样会死循环。
bool normal_bs(int *a, int k, int s, int arrlen){
int l = 0, r = (arrlen-1), m;
while((l) < (r)){
m = ((l)+(r))>>1;
if( a[m] == s ){
return true;
}else if( s > a[m]){
l = (m+1);
}else if( s < a[m]){
r = (m-1);
}
}
return false;
}
一个较好的二分查找的程序,一般二分停止条件是l与r发生了位置交换,而发生位置交换前,l==r的情形一定会出现的。所以只需要如下的做:
bool normal_bs(int *a, int k, int s, int arrlen){
int l = 0, r = (arrlen-1), m;
while(true){if( l==r ){ // 循环停止条件
if(a[l] == s) return true;
else return false;
}
m = ((l)+(r))>>1;
if( a[m] == s ){
return true;
}else if( s > a[m]){
l = (m+1);
}else if( s < a[m]){
r = (m-1);
}
}
return false;
}
个人觉得这个写法是较之于之前写法更优的一个写法。
一下是循环数组+二分查找的示例。要加强自己的努力啊,太弱了。呵呵~
//============================================================================
// Name : binarySearch.cpp
// Author : jry
// Version :
// Copyright : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================
#include <iostream>
using namespace std;
bool NAIVE_DEBUG = false;
bool find(int *a, int k, int s, int arrlen){
int l = (0+k)%arrlen, r = (arrlen-1+k)%arrlen, m;
while(true){
if( NAIVE_DEBUG ) cout << a[l] << "," << a[r] << endl;
if( l == r ) {
if( a[l] == s ) return true;
else return false;
}
//the middle element.
m = ((l-k+arrlen)%arrlen + (r-k+arrlen)%arrlen)>>1; // logic index
m = (m+k)%arrlen; // physical index
if( a[m] == s ){
return true;
}else if( a[m] < s ){
l = (m+1+arrlen)%arrlen;
}else if( a[m] > s ){
r = (m-1+arrlen)%arrlen;
}
}
return false;
}
bool normal_bs(int *a, int k, int s, int arrlen){
int l = 0, r = (arrlen-1), m;
while((l) < (r)){
m = ((l)+(r))>>1;
if( a[m] == s ){
return true;
}else if( s > a[m]){
l = (m+1);
}else if( s < a[m]){
r = (m-1);
}
}
return false;
}
int main() {
int a[] = {4, 5, 6, 7, 8, 1, 2, 3, 4}, k = 5;
int b[] = {6, 0, 6, -11110, 3, 1, 10}, len = sizeof(a)/sizeof(int);
for( int i = 0; i < (int)(sizeof(b)/sizeof(int)); i ++){
if(find(a, k, b[i], len))
cout << b[i] << " in array." << endl;
else
cout << b[i] << " not in array." << endl;
}
cout << "!!!DONE!!!" << endl; // prints !!!Hello World!!!
return 0;
}