Binary Search二分查找

来源:互联网 发布:算法导论13章答案 编辑:程序博客网 时间:2024/03/28 22:38

说明:仅仅作回顾练习

话说Binary Serach是CSE中最基本最基本的问题,但是,《编程珠机》里号称“提供充足的时间,竟然仅有10%的专业程序员能将这个小程序编写正确”、“虽然第一篇二分搜索论文在1946年就发表了,但是第一个没有错误的二分搜索程序却直到1962年才实现”,确实很“触目惊心”。

这里只是本人复习中的一些思考过程,并写下来而已。

问题描述:

Given an integer X and integer A0,A1,...,An-1, which are presorted and already in memory, find i such that Ai=X, or return i=-1 if X is not in the input.

我们都知道,对于已经排序的输入,二分查找就是每一次迭代或递归将查找范围缩小一半。

首先,使用递归思考,写伪代码。

这是再典型不过的递归问题,逐渐分解为子问题,直到终止条件(这个问题的递归很容易分析其运行过程,所以看似和迭代的难度差不多,但递归会更自然很多,我觉得)。

int binarySearch(int Goal_Value, int array[], int low, int high){    Step 1:终止条件;    Step 2:分解为子问题;} 

Step2分解为子问题很简单了(刚开始就在这里想当然了),暂时将代码扩展为:

int binarySearch(int Goal_Value, int array[], int low, int high){    Step1:终止条件;    int middle = (low + high)/2;    if ( array[middle] < Goal_Value )        return binarySearch( Goal_Value, array, middle, high );    if ( array[middle] ==Goal_Value )        return middle;    if ( array[middle] > Goal_Value )       return binarySearch(Goal_Value, array, middle, high );} 


现在,将所有的难度转移到终止条件上了。
最开始,想到最极端的情况,二分查找的范围[low, high]缩小至下标相邻,即high=low+1。此时,由于middle = (high+low)/2,在计算机中,整除仍然等于low。
此时,考虑这几种情况:
a)low < X < high
b)high = X
c)low = X
d)X < low
e)X > high
终止条件可针对以上情况分别处理,但难点在于,可能还有些情况没有考虑到,为了简化处理,单独考虑high=low+1的情况即可。
代码如下:

// recursionint binarySearch( int Goal_Value, std::vector<int> array, int low, int high ){if( 1 == (high-low) ){if( array.at(low) == Goal_Value )  return low;if( array.at(high) == Goal_Value )  return high;return -1;}int middle = (low+high)/2;if( array.at(middle) > goal )  return binarySearch( goal, array, low, middle );else if( array.at(middle) < Goal-Value )  return binarySearch( goal, array, middle, high );else  return middle;}

这个版本的有点是很简单,稍微想一会就可以实现,将难度降解到终止条件那几个条件判断语句,但缺点是,没有对查找范围精确控制,没有真正理解二分查找。
有了递归版本,转换成迭代版本就很容易了:

// iterationint binarySearch2( int Goal_Value, std::vector<int> array ){int low = 0;int high = array.size()-1;while( (high-low) != 1 )  // exit {int middle = (low+high)/2;if( array.at(middle) > Goal_Value )  high = middle;else if( array.at(middle)< Goal_Value )  low = middle;else   return middle;}if( array.at(low) == Goal_Value )  return low;if( array.at(high) == Goal_Value )  return high;return -1;}

好,我们来看看标准库中的版本:

/*  * Performs the standard binary search using two comparisions per level. * Returns index where item is found or -1 if not found. * */template<typename Comparable>int binarySearch( const std::vector<Comparable> &array, const Comparable &Goal_Value){int low = 0;int high = array.size()-1;while( low <= high ){int mid = ( low + high ) / 2;if(array.at(mid) < Goal_Value )  low = mid + 1;else if( array.at(mid) > Goal_Value )  high = mid -1;else   return mid;}return -1;}


曾经第一次看到这个标准实现时,困惑了好久。为什么low <= high就退出?为什么middle所在的元素值小于X时下标low=mid+1?为什么middle所在的元素值大于X时下表high=low-1?几乎和自己刚开始想得完全不同嘛。
其实,仔细想想,这确实是最正宗的。
如果array.at(mid) < Goal_Value,那么查找下界当然应该是mid+1,没必要留给下一次再去比较array[mid]与Goal_Value的值.
如果array.at(mid) > Goal_Value,那么查找上界当然应该是mid-1.
这么定义时,退出条件就可以是low <= high啦。


博客http://www.cppblog.com/converse/archive/2009/09/21/96893.html讲解了闭区间和开区间两种版本,会更好的理解二分查找。

只是记录下这个简单的过程,仅供参考。