二分查找——没有想象中的容易(详解各种变式,超深度理解,c++)
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++)的更多相关文章
- JavaScript正则表达式详解(二)JavaScript中正则表达式函数详解
二.JavaScript中正则表达式函数详解(exec, test, match, replace, search, split) 1.使用正则表达式的方法去匹配查找字符串 1.1. exec方法详解 ...
- Android中Intent组件详解
Intent是不同组件之间相互通讯的纽带,封装了不同组件之间通讯的条件.Intent本身是定义为一个类别(Class),一个Intent对象表达一个目的(Goal)或期望(Expectation),叙 ...
- Objective-C中的@Property详解
Objective-C中的@Property详解 @Property (属性) class vairs 这个属性有nonatomic, strong, weak, retain, copy等等 我把它 ...
- Django--filter()-字段查找(双下划线的使用详解)
Django--filter()-字段查找(双下划线的使用详解) 在了解django中的字段查找的同时,让我们先熟悉一下比较符: 大于--gt-(greater than) 小于--lt-(less ...
- C++中的STL中map用法详解(转)
原文地址: https://www.cnblogs.com/fnlingnzb-learner/p/5833051.html C++中的STL中map用法详解 Map是STL的一个关联容器,它提供 ...
- Javascript中prototype属性详解 (存)
Javascript中prototype属性详解 在典型的面向对象的语言中,如java,都存在类(class)的概念,类就是对象的模板,对象就是类的实例.但是在Javascript语言体系中,是不 ...
- ALSA声卡驱动中的DAPM详解之四:在驱动程序中初始化并注册widget和route
前几篇文章我们从dapm的数据结构入手,了解了代表音频控件的widget,代表连接路径的route以及用于连接两个widget的path.之前都是一些概念的讲解以及对数据结构中各个字段的说明,从本章开 ...
- [转载]java中import作用详解
[转载]java中import作用详解 来源: https://blog.csdn.net/qq_25665807/article/details/74747868 这篇博客讲的真的很清楚,这个作者很 ...
- Spring中的BeanPostProcessor详解
Spring中的BeanPostProcessor详解 概述 BeanPostProcessor也称为Bean后置处理器,它是Spring中定义的接口,在Spring容器的创建过程中(具体为Bean初 ...
随机推荐
- delphi 设置默认控件得到焦点
如果同一窗体有多个按钮的话,追踪源码发现最后是taborder来的 如: 在空白窗体上拖入两个button (btn1,btn2) 如果在btn2设置default = True 运行后,默认焦点还是 ...
- python之路《七》文件的处理
其实我觉得python的方便之处就是简便特别是对数据的处理上 当我们要处理一些数据的时候免不了的就是要处理许多的文件那么下面就让我们来看一下文件的处理 1.打开文件 python的文件打开方式较为简单 ...
- 手写一个Web服务器,极简版Tomcat
网络传输是通过遵守HTTP协议的数据格式来传输的. HTTP协议是由标准化组织W3C(World Wide Web Consortium,万维网联盟)和IETF(Internet Engineerin ...
- echarts折线图,数据切换时(最近七天)绘图不合理现象
echarts折线图,当进行数据切换时存在绘制不合理的问题,数据没错,但绘制不对. 两个0之间的连线应该是平滑直线,如图: 正确的显示: 解决: 在myCharts.setOption(option) ...
- 破解版的OCR文字识别软件,真的好用吗?
很多小伙伴在下载OCR文字识别软件时,会习惯性去找破解版的软件.那么到底什么是破解版的软件呢?其实破解的软件,都是通过非法的手段,破除正版软件的安全权限制作而成的.因此,使用这些破解软件会存在很多安全 ...
- guitar pro系列教程(三):Guitar Pro7乐谱页面显示模式设置
大家好,又到了guitar pro系列教程的时间 本章节我们采用图文结合的方式为大家讲解一下guitar pro 7乐谱的页面显示设置,有兴趣的小伙伴都可以进来看看哦.首让我们先看下图: 如上图所示, ...
- 如何在MathType输入手写体a
作为强大的数学公式编辑器,MathType中还能设置各种样式,还支持自定义设置,给大家编辑公式提供了更多的便利.那么有用户问:要如何将输入的字母a变为手写体呢?下面就来一起学习. 输入手写体a的步骤如 ...
- 【海思】Hi3531A SPI功能的详细配置以及使用
目录 一.前言 二.SPI管脚信息获取 2.1 SPI_SCLK.SPI_SDI.SPI_SDO管脚复用寄存器 2.2 片选SPI_CSN0-SPI_CSN3管脚寄存器 三.配置和使能与SPI相关的管 ...
- 【初等数论】裴蜀定理&扩展欧几里得算法
裴蜀定理: 对于\(a,b\in N^*, x, y\in Z\),方程\(ax+by=k\)当且仅当\(gcd(a, b)|k\)时有解. 证明: 必要性显然. 充分性:只需证明当\(k=gcd(a ...
- Java基础教程——线程局部变量
线程局部变量 ThreadLocal,线程局部变量,不提供锁,不做线程共享,而是为每个线程提供变量的独立副本. import java.util.concurrent.*; public class ...