Binary Search二分查找

说明:仅仅作回顾练习

话说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的情况即可。
代码如下:

// recursion
int 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;
}

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

// iteration
int 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讲解了闭区间和开区间两种版本,会更好的理解二分查找。

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值