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. 你的旧版本 App 为何运行在 iPhone 12 上没有异常?

    背景 当我在 10月14日 iPhone 12 系列发布直播,看到 iPhone 12 系列的分辨率后,我注意到这些分辨率是全新的时,我立即在群里吐槽:又需要适配一波了.我只是以为宽高变化会导致字号变 ...

  2. fork()系统调用后,对于open()前后父子进程的访问

    一开始我也不是很懂,后来看了一篇别人的博客觉得写得特别好,现在拷贝下来分享一下. 如果换成write函数 如果换成write函数,先open()后fork(),那么父子进程共享文件描述符,即使在子进程 ...

  3. gdb 符号表 &信息 &工具

    查看二进制文件的编译器版本 strings  info.o |grep GCCGCC: (crosstool-NG linaro-1.13.1-2012.02-20120222 - Linaro GC ...

  4. Andrew Ng 机器学习公开课 - 线性回归

    我的机器学习系列从现在开始将会结合Andrew Ng老师与sklearn的api是实际应用相结合来写了. 吴恩达(1976-,英文名:Andrew Ng),华裔美国人,是斯坦福大学计算机科学系和电子工 ...

  5. ceph luminous版本限制osd的内存使用

    引言 ceph自从到了L版本以后,L版本的启用,对性能本身有了极大的提高,一直对这个比较不放心的就是内存的占用,刚开始的时候记得大量dd就可以把内存搞崩掉,这个应该是内部的设计逻辑需要更多的内存的占用 ...

  6. nginx开启目录浏览

    使用nginx作为下载站点,开启目录浏览的功能 在/etc/nginx/sites-enabled/default中添加: autoindex on ; autoindex_exact_size of ...

  7. Linux(centos6.8)配置Tomcat环境

    1.下载Linux版的Tomcat包 (1)通过官方下载 tomcat官方:https://tomcat.apache.org/download-80.cgi (2)通过分享下载 如网盘分享等途径 2 ...

  8. 面试官:小伙子,你给我说一下Java中什么情况会导致内存泄漏呢?

    概念 内存泄露:指程序中动态分配内存给一些临时对象,但对象不会被GC回收,它始终占用内存,被分配的对象可达但已无用.即无用对象持续占有内存或无用对象的内存得不到及时释放,从而造成的内存空间浪费. 可达 ...

  9. 面试阿里,字节跳动,华为必须知道的Java创建对象的5种方式

    Java创建对象的5种方式 1.直接new,调用了构造器2.通过clone(),没有调用构造器3.通过反射,调用了构造器4.通过反序列化,没有调用构造器5.通过Unsafe类的allocateInst ...

  10. 使用Camtasia创作抖音卡点视频

    空闲的时候刷一刷抖音相信已经成为很多人的日常啦,抖音里面的视频形式多种多样,而其中的卡点视频更是被大家热烈追捧.如果你外出旅行拍摄了很多好看的照片,就很适合用卡点视频的形式展现出来. 如果你想要制作这 ...