华师一附中OI组

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 1325|回复: 4
打印 上一主题 下一主题

二分查找的详细讨论

[复制链接]

738

主题

1485

帖子

5420

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
5420
跳转到指定楼层
#
发表于 2018-4-21 18:33:08 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
在一个有序的表里进行查找的话没有必要使用顺序查找,比如在数组(1,3,5,7,9,11,13,15,17,19)十个数中查找数X=11,我们可以使用二分查找:
初始设起点L=1,终点R=10,没有找到标记B=1。
1、枚举中间位置,M=(L+R)/2=5。这个数字 A(M)=9,9<11,说明要找的数字X在M的右边,那么更新查找起点L=M+1=6,R不变,(想想为什么是M+1)
2、枚举中间位置,M=(L+R)/2=8。A(8)=15,比X小,说明要找的数字X在M的左边,更新查找的终点R=M-1=7,L不变,
3、枚举中间位置,M=(L+R)/2=6。这次找到了A(6)=11。退出查找过程。
这个程序有两种实现,一种是用递归,一种是用迭代。代码如下:
  1. ///递归的二分查找
  2. int bs(int x,int l,int r)
  3. {
  4.     if (r<l) return -1;
  5.     else
  6.     {
  7.         int m=(l+r)/2;   ///m=l+(r-l)/2;
  8.         if (a[m]==x) return m;
  9.         else if (a[m]>x) return bs(x,l,m-1);  ///小了就找左边
  10.         else if (a[m]<x) return bs(x,m+1,r);///大了就找右边
  11.     }
  12. }
复制代码
  1. ///迭代的二分查找
  2. int bs2(int x,int l,int r)
  3. {
  4.     int m;
  5.     bool b=true;///未找到标记
  6.     while (l<=r && b)
  7.     {
  8.         m=(l+r)/2;
  9.         if (a[m]==x) b=false; ///找到了
  10.         else if (a[m]>x) r=m-1;///小了就找左边
  11.         else l=m+1;///大了就找右边
  12.     }
  13.     if (!b) return m;
  14.     else return -1;
  15. }
复制代码





回复

使用道具 举报

738

主题

1485

帖子

5420

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
5420
地板
 楼主| 发表于 2020-7-17 10:38:45 | 只看该作者
经典训练题:

P1024   一元三次方程求解 https://www.luogu.org/problemnew/show/P1024 我的解法参考 http://www.hsyit.cn/forum.php?mo ... 5793&highlight=1024
P1163   银行贷款 https://www.luogu.org/problemnew/show/P1163 我的解法参考 http://www.hsyit.cn/forum.php?mo ... 5796&highlight=1163
回复 支持 反对

使用道具 举报

738

主题

1485

帖子

5420

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
5420
板凳
 楼主| 发表于 2020-7-17 10:35:00 | 只看该作者
还有一种查找是在实数域中查找,比如求根号2,精确到小数点后3位。实数是不好直接判断相等的,我们一般在循环里面加上B标记,当abs(L-R)<=EPS的时候,B标记置位,不再继续查找,请看程序。
回复 支持 反对

使用道具 举报

738

主题

1485

帖子

5420

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
5420
沙发
 楼主| 发表于 2018-9-16 21:37:12 | 只看该作者
  1. /*
  2. 几种二分方法整理
  3. 元素可以重复
  4. */

  5. //lower_bound(num, num+size, x)-num:大于等于x的第一个数的下标
  6. //upper_bound(num, num+size, x)-num:大于x的第一个数的下标

  7. //1.求等于x的最小的index,不存在返回-1
  8. int binary (int *num, int start, int end, int x) {
  9.         int l = start, r = end, ans=-1;
  10.         while(l <= r) {
  11.                 int mid = (l+r) >> 1;
  12.                 if(num[mid] == x) {
  13.                         ans = mid;
  14.                         r = mid - 1;
  15.                 }
  16.                 else if(num[mid] > x)
  17.                         r = mid - 1;
  18.                 else
  19.                         l = mid + 1;
  20.         }
  21.         return ans;
  22. }

  23. //2.求等于x的最大的index,不存在返回-1
  24. int binary (int *num, int start, int end, int x) {
  25.         int l = start, r = end, ans=-1;
  26.         while(l <= r) {
  27.                 int mid = (l+r) >> 1;
  28.                 if(num[mid] == x) {
  29.                         ans = mid;
  30.                         l = mid + 1;
  31.                 }
  32.                 else if(num[mid] > x)
  33.                         r = mid - 1;
  34.                 else
  35.                         l = mid + 1;
  36.         }
  37.         return ans;
  38. }

  39. //3.求小于x的最大的index
  40. int binary (int *num, int start, int end, int x) {
  41.         int l = start, r = end;
  42.         while(l <= r) {
  43.                 int mid = (l+r) >> 1;
  44.                 if(num[mid] >= x)
  45.                         r = mid - 1;
  46.                 else
  47.                         l = mid + 1;
  48.         }
  49.         return r;
  50. }

  51. //4.求大于x的最小的index
  52. int binary (int *num, int start, int end, int x) {
  53.         int l = start, r = end;
  54.         while(l <= r) {
  55.                 int mid = (l+r) >> 1;
  56.                 if(num[mid] <= x)
  57.                         l = mid + 1;
  58.                 else
  59.                         r = mid - 1;
  60.         }
  61.         return l;
  62. }

  63. //5.求大于等于x的最小的index
  64. int binary (int *num, int start, int end, int x) {
  65.         int l = start, r = end;
  66.         while(l <= r) {
  67.                 int mid = (l+r) >> 1;
  68.                 if(num[mid] >= x)
  69.                         r = mid - 1;
  70.                 else
  71.                         l = mid + 1;
  72.         }
  73.         return l;
  74. }

  75. //6.求小于等于x的最大的index
  76. int binary (int *num, int start, int end, int x) {
  77.         int l = start, r = end;
  78.         while(l <= r) {
  79.                 int mid = (l+r) >> 1;
  80.                 if(num[mid] <= x)
  81.                         l = mid + 1;
  82.                 else
  83.                         r = mid - 1;
  84.         }
  85.         return r;
  86. }
复制代码
回复 支持 反对

使用道具 举报

738

主题

1485

帖子

5420

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
5420
楼主
 楼主| 发表于 2018-7-15 14:23:13 | 只看该作者
二分查找的思路很简单,但是编程实现中还是会有很多的细节,现在思考几个问题,就以上面那个在(1,3,5,7,9,11,13,15,17,19)十个数中查找数X为例,假设L=1 R=10。
1、设X=11,找到了在第6位,查找完毕后L和R的值分别是多少?
2、设X=10,找不到X,退出循环的时候L和R的值分别是多少?
3、B标记到底有没有必要?



自然数域里的查找是精确的,找到找不到是明确,但是实数范围内的查找由于精度的问题,需要做特殊的处理,比如我们要求根号2,设为X,那么X=1.414吗?当要求的精度只有小数点后三位的时候的确是这样的,所以我们可以在程序里面加上一个判断精度是否达到的语句,只要精度达到就可以退出,而不是一定要求a(m)==X。



回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|服务支持:DZ动力|华师一附中OI组  

GMT+8, 2024-11-2 06:33 , Processed in 0.101698 second(s), 23 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表