你真的能搞定二分查找吗 — 二分查找及变形

看到一篇很有意思的文章就转过来了。。。

原文:

首先引用一下《编程珠玑》中的两句话: 

  1. 尽管给了那么充裕的时间,只有大约10%的专业程序员能够写出正确的二分查找。
  2. 尽管第一个二分查找程序于1946年就公布了,但是第一个没有bug的二分查找程序在1962年才出现。

当时看到这的时候,我觉得有点夸张。这里不去讨论是否真的花了20年才人们才写出正确的代码,但这两句话至少告诉我们,不要小看二分搜索。

确实,写过bsearch的人都知道,迭代的出口很难在短时间内想清楚(反正我经常糊涂)。当然,常规的二分搜索大家现在基本都能写了,(写过这么多次,背也背下来了),但能写出来不代表真正理解了二分搜索。比如我今天在做一道题的时候就卡在了这里:从一个有序数组中找到大于x且最接近x的数。

说白了就是二分搜索,但是迭代的出口是什么?

先来看一下常规的二分查找的代码:

[cpp]  view plain  copy
 print ?
  1. /** 
  2.  * 函数简介: 有序数组中搜索指定元素 
  3.  * 
  4.  * 参数: a  数组指针 
  5.  * 参数: n  元素数目 
  6.  * 参数: x  要搜索的值 
  7.  * 
  8.  * 返回值:  若找到,返回该元素下标 
  9.  *          若找不到,返回-1 
  10.  */  
  11. /* ---------------------------------------------------------------*/  
  12. int bsearch(int *a, int n, int x)  
  13. {  
  14.     int m;  
  15.     int l = 0, r = n -1;  
  16.    
  17.     while( r >= l )  
  18.     {  
  19.         m = (l+r)/2;  
  20.         if(a[m] == x)  
  21.             return m;  
  22.         else if(a[m] < x)  
  23.             l = m+1;  
  24.         else  
  25.             r = m-1;  
  26.     }  
  27.     return -1;  
  28. }  

考虑一下要查找的元素不在序列中的情况:函数会返回-1,那l和r分别指向什么呢?

可以证明:l指向大于x的第一个元素,r指向小于x的第一个元素。(为什么?迭代的最后一步必然是l和r指向同一元素,然后为什么会变得比r大呢?想想就明白了)

既然得出了这个结论,那么代码就好写了,但是还得注意一些边界条件,比如最后返回的时候检查l和r是否越界,如果越界就说明找不到。

代码如下,如果有错,欢迎讨论:

[cpp]  view plain  copy
 print ?
  1. #include  <iostream>  
  2. using namespace std;  
  3.    
  4. /* ---------------------------------------------------------------*/  
  5. /** 
  6.  * 函数简介: 有序数组中搜索指定元素 
  7.  * 
  8.  * 参数: a  数组指针 
  9.  * 参数: n  元素数目 
  10.  * 参数: x  要搜索的值 
  11.  * 
  12.  * 返回值:  若找到,返回该元素下标 
  13.  *          若找不到,返回-1 
  14.  */  
  15. /* ---------------------------------------------------------------*/  
  16. int bsearch(int *a, int n, int x)  
  17. {  
  18.     int m;  
  19.     int l = 0, r = n -1;  
  20.    
  21.     while( r >= l )  
  22.     {  
  23.         m = (l+r)/2;  
  24.         if(a[m] == x)  
  25.             return m;  
  26.         else if(a[m] < x)  
  27.             l = m+1;  
  28.         else  
  29.             r = m-1;  
  30.     }  
  31.     return -1;  
  32. }  
  33.    
  34. /* ---------------------------------------------------------------*/  
  35. /** 
  36.  * 函数简介:  在有序数组中查找比x大但是最接近x的数 
  37.  * 
  38.  * 参数: a  数组指针 
  39.  * 参数: n  元素数目 
  40.  * 参数: x  要搜索的值 
  41.  * 
  42.  * 返回值:  若找到,返回该元素下标 
  43.  *          若找不到,返回-1 
  44.  */  
  45. /* ---------------------------------------------------------------*/  
  46. int bsearch_more(int *a,int n, int x)  
  47. {  
  48.     int m;  
  49.     int l = 0, r = n -1;  
  50.    
  51.     while( r >= l )  
  52.     {  
  53.         m = (l+r)/2;  
  54.           
  55.         if(a[m] == x)  
  56.             return ( (m+1) >= n ? -1 : (m+1) );  
  57.         else if(a[m] < x)  
  58.             l = m+1;  
  59.         else  
  60.             r = m-1;  
  61.     }  
  62.     l = (l >= n ? -1 : l);  
  63.     return l;  
  64. }  
  65.    
  66. /* ---------------------------------------------------------------*/  
  67. /** 
  68.  * 函数简介:  在有序数组中查找比x小但是最接近x的数 
  69.  * 
  70.  * 参数: a  数组指针 
  71.  * 参数: n  元素数目 
  72.  * 参数: x  要搜索的值 
  73.  * 
  74.  * 返回值:  若找到,返回该元素下标 
  75.  *          若找不到,返回-1 
  76.  */  
  77. /* ---------------------------------------------------------------*/  
  78. int bsearch_less(int *a,int n, int x)  
  79. {  
  80.     int m;  
  81.     int l = 0, r = n -1;  
  82.    
  83.     while( r >= l )  
  84.     {  
  85.         m = (l+r)/2;  
  86.         if(a[m] == x)  
  87.             return ( (m-1) < 0 ? -1 : (m-1) );  
  88.         else if(a[m] < x)  
  89.             l = m+1;  
  90.         else  
  91.             r = m-1;  
  92.     }  
  93.     return r;  
  94. }  
  95.    
  96. int main(int argc, char *argv[])  
  97. {  
  98.     int x;  
  99.     int arr[] = {8, 17, 26, 32, 40, 72, 87, 99};  
  100.     while(cin >> x)  
  101.     cout << bsearch_less(arr,8,x)+1   
  102.         << " "  
  103.         << bsearch_more(arr,8,x)+1<< endl;  
  104. }  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值