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. a^b(取模运算)

    a^b(sdtbu oj 1222) Description 对于任意两个正整数a,b(0 <= a, b < 10000)计算ab各位数字的和的各位数字的和的各位数字的和的各位数字的和. ...

  2. Redis 未授权访问漏洞批量提权

    一.getshell前提 ①能有对 /root/.ssh/目录写入的权限 ②目标机开启22端口 二.安装依赖 sudo easy_install redis 三.使用 redis python hac ...

  3. [PHP安全特性学习]strcmp()函数安全漏洞

    简介 PHP函数的安全特性-strcmp() 函数 php-strcmp()函数 PHP strcmp() 函数 strcmp() 函数比较两个字符串. 注释:strcmp() 函数是二进制安全的,且 ...

  4. ctf-工具-binwalk

    binwalk在玩杂项时是个不可缺的工具.1.最简单的,在玩隐写时,首先可以用它来找到其中的字符串例如:在铁人三项,东北赛区个人赛中,有一道题它直接给了一个文件,没有后缀,不知道是什么文件先binwa ...

  5. Win10 安装MySQL 5.7.32(解压版)

    Win10 安装MySQL 5.7.32(解压版) MySQL 5.7.32 下载 官网下载(速度慢,不推荐使用):https://dev.mysql.com/downloads/mysql/ 清华镜 ...

  6. 工作中使用mongodb

    写了一个mongodb的基类 1 <?php 2 3 namespace BI\Service\MongoDB; 4 5 use MongoDB\Driver\BulkWrite; 6 use ...

  7. Pytest学习(十一)- 失败重跑插件pytest-rerunfailures的使用

    环境依赖 Python 3.5, 最高 3.8, or PyPy3 pytest 5.0或更高版本 插件安装 pip3 install pytest-rerunfailures -i http://p ...

  8. 对KVC和KVO的理解

    html { overflow-x: initial !important } :root { --bg-color: #ffffff; --text-color: #333333; --select ...

  9. echarts柱状图接口提供的数据是数值,要在顶部展示百分比

    查阅echarts配置文档,柱状图并没有类似于饼图的直接展示百分比的参数,所以需要自己配置. window.onload = function () { var list1=[25.02,19.76, ...

  10. kafka producer 概要(看源码前,最好能掌握)

        kafakproducer概要(看源码前,最好能理解) 摘要 kafak 被设计用来作为一个统一的平台来处理庞大的数据的实时工具,在设计上有诸多变态的要求 它必须具有高吞吐量才能支持大量事件流 ...