int binarySearch(int[] nums, int target) {
int left = 0;
int right = nums.length - 1; // 注意 while(left <= right) {
//int mid = (right + left) / 2; //不建议使用
int mid = left + (right-left)/2;
if(nums[mid] == target)
return mid;
else if (nums[mid] < target)
left = mid + 1; // 注意
else if (nums[mid] > target)
right = mid - 1; // 注意
}
return -1;
}

这就是最基本的二分查找,若查找成功,则立即返回target下标mid,否则返回-1

老生常谈的细节:

(1)mid = left + (right-left)/2 , 之所以这样写,避免了right与left相加除以2,分子相加时可能超过INT_MAX范围的问题

(2)right = 数组长度-1 时,while中一定对应的是left<=right , 因为每次搜索都是在[left, right]闭区间上,只有left!=right 才会停止查找

(3)建议先判断一下数组是否为空(empty)

(4)不存在target时,一定返回-1

(5)理解一下mid = left + (right-left)/2 总是返回中间数偏向于left的一侧(自己理解一下,有用)

然后,就是变式

如果我一定要写成:right = 数组长度呢?我怎么修改代码呢?(这就是为什么网上会有两种模板)

模板如下,改动地方有三个:

1. right = 数组长度(不要减一)

2.while判断条件是 left<right(而不是left<=right)

3. right = m(不是right = mid +1)

 1 int main()
2 {
3 vector<int> v={1,2,3,4,5,6,7};
4 int key = 9,index=-1;
5 int l=0,r=v.size();
6 while(l<r)
7 {
8 int m =l+(r-l)/2;
9 if(v[m]==key)
10 {
11 index = m;
12 break;
13 }
14 else if(v[m]<key)
15 l = m+1;
16 else
17 r = m;
18 }
19 cout<<"index:"<<index<<endl;
20 return 0;
21 }

第二份代码和第一份效果一模一样,如何理解呢?

第二份中,r设定为数组长度,所以搜索空间是[left, right) , 当left==right时,就已经可以退出了,所以while中写left<right。如果mid值与key值不相等,那么由于mid值已经搜索过了,

所以下次检索时,是从[left,mid) 或 [mid+1,right) 开始,所以写成了right = mid(而不是right = mid +1)

继续变式:

(1)想要返回第一个等于key的下标,如{1,2,3,3,3,4}中,key=3,则返回下标

find_first_equal

 1 int find_first_equal(vector<int> v, int key)
2 {
3 int l=0, r =v.size()-1;
4 int index;
5 while(l<=r)
6 {
7 int m = l+(r-l)/2;
8 if(v[m]>=key)
9 r = m-1;
10 else
11 l = m+1;
12 }
13 if(l>=0&&l<=v.size()-1&&v[l]==key)
14 index = l;
15 else
16 index = -1;
17 cout<<"find_first_equal,index:"<<index<<endl;
18 return index;
19 }

技术关键点在于:

第一,退出的时候,我们要的是left还是right,想一想。

第二,如果key值不存在,那么left可能不合法,所以要判断left在正常范围内,并且要和key对上。

(2)想要返回最后一个等于key的下标,如{1,2,3,3,3,4}中,key=3,则返回下标4

find_final_equal

 1 int find_final_equal(vector<int> v, int key)
2 {
3 int l=0, r =v.size()-1;
4 int index;
5 while(l<=r)
6 {
7 int m = l+(r-l)/2;
8 if(v[m]<=key)
9 l = m+1;
10 else
11 r = m-1;
12 }
13 if(r>=0&&r<=v.size()-1&&v[r]==key)
14 index = r;
15 else
16 index = -1;
17 cout<<"find_final_equal,index:"<<index<<endl;
18 return index;
19 }

(3)想要返回第一个大于key的下标,如{1,2,3,4,5}中,key=3,则返回下标3

find_first_bigger

 1 int find_first_bigger(vector<int> v, int key)
2 {
3 int l=0, r =v.size()-1;
4 int index;
5 while(l<=r)
6 {
7 int m = l+(r-l)/2;
8 if(v[m]<=key)
9 l = m+1;
10 else
11 r = m-1;
12
13 }
14 if(l<=v.size()-1)
15 index = l;
16 else
17 index = -1;
18 cout<<"find_first_bigger,index:"<<index<<endl;
19 return index;
20 }

参考文档:

1.https://blog.csdn.net/asmartkiller/article/details/96179338

2.https://www.cnblogs.com/luoxn28/p/5767571.html

二分查找——没有想象中的容易(详解各种变式,超深度理解,c++)的更多相关文章

  1. JavaScript正则表达式详解(二)JavaScript中正则表达式函数详解

    二.JavaScript中正则表达式函数详解(exec, test, match, replace, search, split) 1.使用正则表达式的方法去匹配查找字符串 1.1. exec方法详解 ...

  2. Android中Intent组件详解

    Intent是不同组件之间相互通讯的纽带,封装了不同组件之间通讯的条件.Intent本身是定义为一个类别(Class),一个Intent对象表达一个目的(Goal)或期望(Expectation),叙 ...

  3. Objective-C中的@Property详解

    Objective-C中的@Property详解 @Property (属性) class vairs 这个属性有nonatomic, strong, weak, retain, copy等等 我把它 ...

  4. Django--filter()-字段查找(双下划线的使用详解)

    Django--filter()-字段查找(双下划线的使用详解) 在了解django中的字段查找的同时,让我们先熟悉一下比较符: 大于--gt-(greater than) 小于--lt-(less ...

  5. C++中的STL中map用法详解(转)

    原文地址: https://www.cnblogs.com/fnlingnzb-learner/p/5833051.html C++中的STL中map用法详解   Map是STL的一个关联容器,它提供 ...

  6. Javascript中prototype属性详解 (存)

    Javascript中prototype属性详解   在典型的面向对象的语言中,如java,都存在类(class)的概念,类就是对象的模板,对象就是类的实例.但是在Javascript语言体系中,是不 ...

  7. ALSA声卡驱动中的DAPM详解之四:在驱动程序中初始化并注册widget和route

    前几篇文章我们从dapm的数据结构入手,了解了代表音频控件的widget,代表连接路径的route以及用于连接两个widget的path.之前都是一些概念的讲解以及对数据结构中各个字段的说明,从本章开 ...

  8. [转载]java中import作用详解

    [转载]java中import作用详解 来源: https://blog.csdn.net/qq_25665807/article/details/74747868 这篇博客讲的真的很清楚,这个作者很 ...

  9. Spring中的BeanPostProcessor详解

    Spring中的BeanPostProcessor详解 概述 BeanPostProcessor也称为Bean后置处理器,它是Spring中定义的接口,在Spring容器的创建过程中(具体为Bean初 ...

随机推荐

  1. 内网渗透 day4-meterpreter基本命令

    meterpreter基本命令 目录 1.getuid 查看当前用户 1 2.getpid 查看当前的进程id 1 3.getsystem 初步提权 1 4.ps 1.查看进程列表2.帮助我们获取pi ...

  2. Flask补充内容

    关键字: 一,过滤器 二,增删改查 一,过滤器 1,概念:过滤器的本质就是函数.有时候我们不仅仅只是需要输出变量的值,我们还需要修改变量的显示,甚至格式化.运算等等,而在模板中是不能直接调用 Pyth ...

  3. c++与c

    const char* c_str ( ) const; Get C string equivalent Generates a null-terminated sequence of charact ...

  4. 基于FFmpeg的Dxva2硬解码及Direct3D显示(二)

    解析视频源 目录 解析视频源 获取视频流 解析视频流 说明:这篇博文分为"获取视频流"和"解析视频流"两个部分,使用的是FFmpeg4.1的版本,与网上流传的低 ...

  5. python pip install指定国内源镜像

    有时候安装一些依赖包,网不好,直接超时,或者这个包就是死都下不下来的时候,可以指定国内源镜像. pip install -i 国内镜像地址 包名 e.g. pip install -i  http:/ ...

  6. 五:key关键字 string字符串 list列表 set集合 Zset有序集合

    key 1.keys 命令用于查找所有符合给定模式 pattern 的 key . keys *  查找所有的key返回一个列表 2.EXISTS xxx 检查是否有指定名字为xxx的key 有就返回 ...

  7. 测试:OGG初始化同步表,源端抽取进程scn<源端事务的start_scn时,这个变化是否会同步到目标库中?

    一.测试目标 疑问,OGG初始化同步表,源端抽取进程开始抽取的scn<源端事务的start_scn时,这个变化是否会同步到目标库中? 二.实验测试 如下进行测试! session 1 SQL&g ...

  8. Mysql事物与二阶段提交

     1.事务的四种特性(ACID) 事务可以是一个非常简单的SQL构成,也可以是一组复杂的SQL语句构成.事务是访问并且更新数据库中数据的一个单元,在事务中的操作,要么都修改,要么都不做修改,这就是事务 ...

  9. 创建一个自定义名称的Ceph集群

    前言 这里有个条件,系统环境是Centos 7 ,Ceph 的版本为Jewel版本,因为这个组合下是由systemctl来进行服务控制的,所以需要做稍微的改动即可实现 准备工作 部署mon的时候需要修 ...

  10. Mysql的下载,安装,远程连接,密码加密规则修改。

    第一次接触mysql,,很多地方不懂,出了很多问题.本来应该在Linux系统中安装mysql的,但是奈何各种电脑限制,所以在公司电脑的Windows service R2 系统上装了mysql数据库. ...