二分查找——没有想象中的容易(详解各种变式,超深度理解,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初 ...
随机推荐
- 中介者模式及在NetCore中的使用MediatR来实现
在现实生活中,常常会出现好多对象之间存在复杂的交互关系,这种交互关系常常是"网状结构",它要求每个对象都必须知道它需要交互的对象.例如,每个人必须记住他(她)所有朋友的电话:而且, ...
- 《.NET 5.0 背锅案》第2集:码中的小窟窿,背后的大坑,发现重要嫌犯 EnyimMemcachedCore
在第1集的剧情中,主角是".NET 5.0 正式版 docker 镜像",它有幸入选第1位嫌疑对象,不是因为它的嫌疑最大,而是它的验证方法最简单,只需要再进行一次发布即可.我们在周 ...
- icmp port unreachable
端口不可达: client------>server 结果server回复端口不可达, 由于是icmp报文: 到达client内核协议栈后进入icmp_rcv处理: /* * Deal with ...
- kernel——Makefile, head.S ...
在Makefile中找到的重要信息: (1)连接脚本 通过连接脚本,知道的信息: (1)入口符号 stext (2)入口连接地址 0xC0000000 + 0x00008000 根据入口符号,可以找到 ...
- python之 socketserver模块的使用
在我们正常的使用socket模块来写一个server的程序就会显得比较的复杂通常一般流程为 1.生成socket实例对象 2.绑定地址 3.开始监听 4.接收数据 一般demo为 # 服务器 impo ...
- 线程范围内的环境变量---ThreadLocal
package cn.itcast.heima2; import java.util.HashMap; import java.util.Map; import java.util.Random; p ...
- mysql之binlog和各类日志介绍
1.错误日志 错误日志作用: 记录MySQL的启动.停止信息以及在MySQL运行过程中的错误信息. 参数log_error(默认开启) 修改后重启生效 log_error=[path/[file_n ...
- ubuntu配置bonding
如果节点上有多个网络接口时可以通过bonding将多个网络接口虚拟为一个网络接口,bonding可以提供高可用及负载均衡功能,从而提高节点的网络接口性能及可用性. 配置单bond 一.使用如下命令安装 ...
- 使用Java将XSL和XML文件输出为HTML(XSL学习笔记二)
XSL 指扩展样式表语言(EXtensible Stylesheet Language),前面一篇博客介绍了使用XSL即可直接将XML输出为HTML片段被浏览器解析,但是这样在web应用中浏览器的解析 ...
- Mac中的格式转换如何用读写工具Tuxera NTFS完成
Tuxera NTFS for Mac是一款专门为Mac用户提供的NTFS驱动软件,它不仅可以进行磁盘文件的访问.编辑.传输和存储,还可以对硬盘进行维修检查以及修复. 今天小编就给大家简单介绍一下Tu ...