您的位置:首页 > 其它

二分算法入门——二分查找

2014-11-05 23:30 253 查看

  二分查找,无论是从名字还是理论都十分简单一个算法,其博大精深,简直恐怖。Jon Bentley:90%以上的程序员无法正确无误的写出二分查找代码。

  别人不知道,反正我早上是写了好久,这个查找算法,将查找的复杂度从 o( n ) 降到了 o( logn ) ,当之无愧的的好算法,更是许多高级算法的优化策略之一。

  

  二分查找之基本思路

  虽然二分查找是一个很吊的算法,但是跟很多算法一样,需要使用的基础条件——序列有序!

  先假设一个单调非增序列:1 2 3 4 5 6 ,求找到3的位置,地球人都会马上这么想——一个一个找咯,可是这样很慢!


  于是有人想要加快查找速度,他们发现如果从中间开始找,那么每次比较后就至少可以排除一半的数,这就是二分查找的基本思想——折半。

  1)设置三个指针变量(应该说是指针性质的变量,索引也是可以的)——low ,mid 和 high,并且满足 mid = ( low + high ) / 2;

  2)设置循环,进行target值与mid存储值的比较,根据比较结果更新low或者high;

  3)在 2)中,若出现找到target的情况,则返回mid指针;如果一直找不到,则返回空指针。

  下面是代码:

  

//方法一
int binary_search(int n, int v)
{
int low=0,high=n-1,mid;
while (low<=high)
{
mid=(low+high)/2;
if (a[mid]==v)
return mid;
if (a[mid]<v)
low=mid+1;
if (a[mid]>v)
high=mid-1;
}
return -1;
}


  看起来好像没什么问题,实际上在一些情况下 ,答案会很奇怪:1 2 2 4 5,我们如果找2,答案 -> 2 (这是索引啊!)  

  那么问题来了,为什么是返回第二个,而不是第一个呢?其实很简单,当序列出现重复元素时,我们找到了当然是其中“任意”一个啦!

  但是其实往往我们需要处理的序列总是拥有重复元素的,所以,我们需要优化!

  我们先来分析原来的二分查找 —— left < mid <right ,即我们将“=”的情况全部交给mid处理,于是mid会给你各种答案:

  所以,我们不妨按这种规则来二分 —— left <= mid < right !

  我们可以看看代码:

//方法二:返回第一个位置
int lowerBound(const int a[],const int size,const int target)
{
int low=0,high=size-1,mid;
while(low<high)
{
mid=(low+high)/2;
if(a[mid]<target)
low=mid+1;
else high=mid;
}
if(a[low]==target) return low;
else return -1;
}


  这里我们会发现两种查找方式一个最明显的不同:方法一是找到答案立即返回,方法二则是一直找到最底部(当成树看)然后再返回!感觉方法二要一

  直找到底部好像会很慢,但是事实证明:方法一才是最慢的,所以我们以后可以抛弃第一种写法啦!

  当然,方法二是返回第一个位置,那么就会有返回最后一个位置的算法啦!

  上代码:

//方法三:返回最后一个位置
int upperBound(const int a[], const int size, const int target)
{
int low=0,high=size-1,mid;
while(low<high)
{
mid=(low+high)/2+1;
if(a[mid]>target)
high=mid-1;
else low=mid;
}
if(a[high]==target) return high;
else return -1;
}


  以上就是我们的二分查找算法,参考资料:数据结构与程序设计——C++语言描述(这真是一本好书!)


  

    

  

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: