当前位置: 首页 > news >正文

【优选算法 二分查找】二分查找入门详解:二分查找 & 在排序数组中查找元素的第一个和最后一个位置

     

 


 二分查找


    题目描述    



    题目解析    



    暴力解法    


我们可以从左往右遍历一次数组,如果存在 target 则返回数组的下标,否则返回 -1;

时间复杂度 O(N),因为没有利用数组有序的特点,每次比较只能舍弃一个要比较的数;


    二分查找法    


所以我们可以把 [1 , 4 ] 这个区间舍弃掉,直接看 [ 5 , 8 ];仅仅拿 4 和 5 比较一次,就干掉了一大片数;


    总结    


在一个数组中,随便找一个数,拿这个数和 target 进行比较,并且以拿的这个数分成两个区间,比较后舍弃一部分数,然后再从另一个区间的数中,找下一个要和 target 比较的数;


二分查找算法的本质,就是当数组具有“二段性”时,哪怕这个数组是无序的,只要能在这个数组中发现“二段性”,那么也可以使用二分查找;


    “二段性”     


当我们发现一个规律,然后根据这个规律,选取某一个点,根据这个点,能把数组分成两个区域,根据规律能够有选择地舍弃一部分,进而在另一个部分继续查找的时候,此时,就可以使用二分查找的算法


 

我们要从数组中找和 target 进行比较的数,不一定是先从 n/2 的位置开始找,只要找的这个数的点,能把这个区间分成两部分,即满足二段性,进而使用二分查找法;

但是哪怕那么多点可以选择,但是我们都应该先选择 n/2 的位置的点,因为在概率学的数学期望中,我们选择中间的点,时间复杂度是最好的;


 

  1. nums[mid] < target,left = mid+1
  2. nums[mid] = target,return mid
  3. nums[mid] > target,right = mid-1

    细节问题    


因为当 left = right 的时候,也是要继续判断的,所以 left <= right 为循环判断条件;

时间复杂度,只需要看循环执行多少次:

如何求 x 呢?


int mid = (left  + right) / 2,mid 会有溢出风险:


如果想不从 n/2 开始查找,可以修改,如下面的写法是从 n/3 开始查找的: 


 在排序数组中查找元素的第一个和最后一个位置 




    题目解析     


    发现二段性     


    方法一:利用数组有序特性,使用简单的二分查找法    


如果使用简单的二分查找,创建左指针和右指针,再找中间值,对这个中间值分别进行讨论;如果对于一些特点的情况,如下面的这种情况,这个方法的时间复杂度会非常高;

虽然 mid 指针刚好指向 target,但是不能确定此时的 mid 指向的是在目标连续区域的哪一个位置,从而不好找目标区域的起始位置和终点位置;

所以使用简单二分查找的方法,对于上述情况和类似情况,查找的时间复杂度会近似于暴力查找; 


    方法二:在简单二分查找方法的基础上,对二分策略进行优化    


回到题目的规律:当发现题目具有二段性,就可以利用二分查找;


因为不能同时查找目标区间起始位置和终点位置,所以我们可以先查找起始位置;

利用二段性,我们可以根据目标值起始位置,把数组区间分成两个部分;


左边区域的值都是小于 target,右边区域的值都大于等于target,根据 target,在查询目标区域左端点的时候,发现这个数组是有二段性,因此可以使用二分查找


    查找区间左端点     


    以 mid 的值来决定 left 和 right 的更新方法    


我们设 mid 指向的值为 x,我们要找 >=target 区间的起始位置,拿 x 的值与 target 讨论;


  • 1. x < target,此时 mid 落在 < target 的区域,不可能有最终结果,所以继续往下执行:


  • 2. x >= target

(不同点,之前简单二分查找的方法分三种情况讨论,这里只分两种情况,因为 x= target 并不是最终的结果,因为最终的结果的左端点和右端点)

对于这种情况,对应的做法:

我们虽然[mid , right] 区间的情况一定是都大于 target,但是不知道 [left , mid] 区间的具体情况是都小于 target 还是部分小于 target;所以 right 绝对不能移动到 [left , mid] 这个不确定的区间,因为如果 mid 在修改之前就是目标区间的左端点,让 right = mid -1,[left , right]这个区间就没有 target 了,所以 right = mid 即可;


    处理细节问题   


    1.循环条件    

对于执行循环操作(二分查找操作)的第一个循环条件,是 left <= right,还是 left < right 呢?

一定是选择 left< right ,为什么呢?有两个原因;


  •      原因一:因为 left = right 的情况不需要放到循环中判断    

我们分三种情况来讨论(ret 为返回的最终结果):

  •     1. 数组中有元素等于 target    

前面提到,left 刚开始一直处于 < target 的区间,在 x>= target 的条件下,right=mid,所以right 一定在 ret 右边的区间,threshold 为ret (第一个 target 元素)的前一个元素;

 

因为 left 和 right 的调整方式是 left = mid+1,right = mid,所以 right 一直在>= target 的区域移动,而 left = mid +1,说明 left 是一直想要跳出 < target 的区域的;

所以当 left 和 right 调整到最后,循环结束,此时 left 一定会和 right 相等,此时我们在循环结束后单独判断相遇点和 target 是否相等即可;如果 left = right ,那么两个指针指向的位置,一定就是目标区间左端点 ret,所以循环条件只需要判断 left < right 即可;


  •     2. 数组中所有元素全都大于 target    

如果所有数组元素都大于 target,那么整个过程只有 right 指针在不断向左边移动,直到 left 和 right 相遇;

哪怕相遇,也没有最终结果,所以这种情况下,我们只需要在left < right 时执行循环,循环退出,left=right;

此时我们只需要拿当前两个指针指向的值和 target 比较:相同则回到第一种情况,这个值就是目标区间的左端点;不相同则返回 [-1,-1];


  •    3. 数组中所有元素全都小于 target    

这种情况和第二种情况同理:

整个更新过程都只有 left 在移动,循环退出时,left = right,只需要判断当前指针的值和 target 是否相等即可,相等则这个值就是目标区域的起始位置 ret,否则返回 [-1,-1];


  •    原因二:如果在循环条件中判断 left = right,会出现死循环   

 什么时候会出现死循环,就是整个数组中有元素等于 target 的时候

如果循环条件是 while(left <= right){ ..... },此时left = right ,更新的 mid还是指向 left,right 所在位置,此时满足 x <= target 条件,right=mid,所以 right = left,left 和 right 就会一直停在一个地方循环更新结果,这种情况下就会出现死循环;


    2. 求中点操作    

求中点操作,也就是在定义 left 和 right 之后,求 mid 的操作; 

  • 1. mid = left + ( right - left ) / 2 ;

  • 2. mid = left +( right - left +1 ) /2 ;

这两种求中点的操作,区别在于数组元素是偶数时,更新 mid 的位置不同;这两种方法在简单二分情况,如第一题的情况都是可以用的,但是这种情况下不能用 mid = left + (right - left +1)/2 求中点;


如果是用第二种方法 mid = left + (right - left +1)/2,此时 mid 的位置:


  • 如果上图条件中,target > 3,那么此时 mid < target ,left = mid+1,程序结束,此时是没问题的;


  • 但是,如果 target <=3,那么此时 mid>=target,此时调整 right = mid,就又进入死循环了:


所以,在求左端点时,只能用 mid = left +(right - left )/2 来求中点;


    查找区间右端点    


根据查找右端点,依旧可以把整个区间分成两部分,<=target,>target :


   根据 mid 与 target 的关系决定更新操作    


    1. x <= target    


因为我们要保证在调整 left 和 right 的过程中,右端点一定要在 [left,right] 区间,所以当前这种情况,我们应该移动 left:


    2. x > target    


因为 x > target,所以mid 所在位置一定是在目标区域之外,因此更新 right = mid -1;

    处理细节问题    


    1. 循环条件   

 


    2. 求中点的操作    

循环条件和求左端点相同,不同的是求中点的公式 


  •  1. mid = left + ( right - left ) / 2 ;


  • 2. mid = left +( right - left +1 ) /2 ;


这两种求中点的操作,区别在于数组元素是偶数时,更新 mid 的位置不同;这两种方法在简单二分情况,如第一题的情况都是可以用的,但是这种情况下不能用 mid = left + (right - left )/2 求中点;

为了接下来的操作不和求左端点的时候弄混,要牢记此时要求的是右端点,接下来的假设都是为了能求出右端点:


如果是用第二种方法 mid = left + (right - left )/2,此时 mid 的位置:


  • 如果上图条件中,target < 2,那么此时 mid > target ,right = mid-1,程序结束,此时是没问题的;


  • 但是,如果 target >=3,那么此时 mid>=target,此时调整 left = mid,就又进入死循环了:

 


所以,在求右端点时,只能用 mid = left +(right - left +1 )/2 来求中点,在求右端点时,为什么用这条公式求中点不会出现死循环呢?


用 mid = left +(right - left +1 )/2  来求中点,此时 mid 在 right 的位置,如果此时 x > target,right=mid-1,此时不满足 left<right 的条件,会结束循环;如果 x<= target,left = mid :

此时也不满足 left<right 的条件,也会结束循环,所以在求右端点的时候,用 mid = left +(right - left +1)/2  来求中点,就不会出现死循环;

    编写代码    



    总结二分模板   


     

 

相关文章:

【优选算法 二分查找】二分查找入门详解:二分查找 & 在排序数组中查找元素的第一个和最后一个位置

二分查找 题目描述 题目解析 暴力解法 我们可以从左往右遍历一次数组&#xff0c;如果存在 target 则返回数组的下标&#xff0c;否则返回 -1&#xff1b; 时间复杂度 O(N)&#xff0c;因为没有利用数组有序的特点&#xff0c;每次比较只能舍弃一个要比较的数&…...

WPF编写工业相机镜头选型程序

该程序满足面阵和线阵的要求。 前端代码 <Window x:Class"相机镜头选型.MainWindow" Loaded"Window_Loaded"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml…...

网络安全内容整理二

网络嗅探技术 网络监听 网络监听&#xff0c;也称网络嗅探(Network Sniffing)&#xff1a;在他方未察觉的情况下捕获其通信报文、通信内容的技术 网卡的工作模式&#xff1a; 1.广播模式(Broadcast Mode)&#xff1a;网卡能够接收网络中的广播信息 2.组播模式(Multicast Mo…...

解决git did not exit cleanly (exit code 128)问题

解决 git did not exit cleanly &#xff08;exit code 128&#xff09;问题 1、错误描述2、解决方法2.1 方法一2.2 方法二 1、错误描述 使用TortoiseGit进行操作时&#xff0c;总是提示下述错误。 2、解决方法 2.1 方法一 打开 TortoiseGit -> Settings 点击 Network&…...

Linux入门攻坚——40、Linux集群系统入门-lvs(1)

Cluster&#xff0c;集群&#xff0c;为了解决某个特定问题将多台计算机组合起来形成的单个系统。 这个单个集群系统可以扩展&#xff0c;系统扩展的方式&#xff1a;scale up&#xff0c;向上扩展&#xff0c;更换更好的主机&#xff1b;scale out&#xff0c;向外扩展&…...

momentum 和 weight_decay 的区别

momentum 和 weight_decay 的区别 两者在优化器中的作用不同,主要体现在优化的目的和机制上。 1. momentum(动量) 作用:加速收敛并减少优化过程中的震荡。 机制: momentum 是用于在梯度下降中积累动量的机制。它通过在每一步中综合之前的更新方向,帮助模型在陡峭区域加速…...

Vue 3 + TypeScript进阶用法

在掌握了 Vue 3 和 TypeScript 的基本使用后&#xff0c;你可以进一步探索一些进阶特性和最佳实践。这些包括更复杂的类型定义、自定义 hooks、全局状态管理等。下面是一些关键点&#xff1a; 1. 更复杂的类型定义 Props 和 Emits 的类型 对于组件的 props 和 emits&#xf…...

AbutionGraph-时序向量图谱数据库-快速安装部署

运行环境 1&#xff09;操作系统 最好是使用CentOS7或者Ubuntu18以上系统&#xff0c;不满足的话请升级系统内核gcc版本至8以上版本。 支持所有国产主流操作系统银河麒麟、统信OS、深度等等&#xff0c;均做过兼容性测试&#xff1b; 2&#xff09;CPU 为确保数据库每个进…...

Delphi-HTTP通讯及JSON解析

HTTP POST 请求函数 HttpPost 此函数用于发送带有JSON内容的POST请求到指定的URL&#xff0c;并接收服务器响应。它包括了必要的异常处理&#xff0c;确保在遇到错误时可以记录日志。 参数&#xff1a; sUrl&#xff1a;目标URL。sJson&#xff1a;要发送的JSON格式字符串。 返…...

Postgres 如何使事务原子化?

原子性&#xff08;“ACID”意义上的&#xff09;要求 对于对数据库执行的一系列操作&#xff0c;要么一起提交&#xff0c;要么全部回滚&#xff1b;不允许中间状态。对于现实世界的混乱的代码来说&#xff0c;这是天赐之物。 这些更改将被恢复&#xff0c;而不是导致生产环境…...

[Vue3]简易版Vue

简易版Vue 实现ref功能 ref功能主要是收集依赖和触发依赖的过程。 export class Dep { // 创建一个类&#xff0c;使用时使用new Depconstructor(value) { // 初始化可传入一个值this._val value;this.effects new Set(); //收集依赖的容器&#xff0c;使用set数据结构}…...

ElasticSearch学习记录

服务器操作系统版本&#xff1a;Ubuntu 24.04 Java版本&#xff1a;21 Spring Boot版本&#xff1a;3.3.5 如果打算用GUI&#xff0c;虚拟机安装Ubuntu 24.04&#xff0c;见 虚拟机安装Ubuntu 24.04及其常用软件(2024.7)_ubuntu24.04-CSDN博客文章浏览阅读6.6k次&#xff0…...

LabVIEW算法执行时间评估与Windows硬件支持

在设计和实现复杂系统时&#xff0c;准确估算算法的执行时间是关键步骤&#xff0c;尤其在实时性要求较高的应用中。这一评估有助于确定是否需要依赖硬件加速来满足性能需求。首先需要对算法进行时间复杂度分析并进行实验测试&#xff0c;了解其在Windows系统中的运行表现。根据…...

经验帖 | Matlab安装成功后打不开的解决方法

最近在安装Matlab2023时遇到了一个问题&#xff1a; 按照网上的安装教程成功安装 在打开软件时 界面闪一下就消失 无法打开 但是 任务管理器显示matlab在运行中 解决方法如下&#xff1a; matlab快捷方式–>右键打开属性–>目标 填写许可证文件路径 D:\MATLAB\MatlabR20…...

Ubuntu Linux 文件、目录权限问题

本文为Ubuntu Linux操作系统- 第五弹 此文是在上期文件目录的内容操作基础上接着讲权限问题 上期回顾&#xff1a;Ubuntu Linux 目录和文件的内容操作 文件访问者身份与文件访问权限 Linux文件结构 所有者&#xff08;属主&#xff09;所属组&#xff08;属组&#xff09;其他…...

LabVIEW密码保护与反编译的安全性分析

在LabVIEW中&#xff0c;密码保护是一种常见的源代码保护手段&#xff0c;但其安全性并不高&#xff0c;尤其是在面对专业反编译工具时。理论上&#xff0c;所有软件的反编译都是可能的&#xff0c;尽管反编译不一定恢复完全的源代码&#xff0c;但足以提取程序的核心功能和算法…...

yolo11经验教训----之一

一、格式转换 可以把python中的.pt文件&#xff0c;导出为libtorch识别的格式&#xff1a; model YOLO("yolo11n.pt") model.export(format"torchscript") 二、查看结构 在c中&#xff0c;我用qt&#xff0c;这样做的&#xff1a; #include "…...

异步处理优化:多线程线程池与消息队列的选择与应用

目录 一、异步处理方式引入 &#xff08;一&#xff09;异步业务识别 &#xff08;二&#xff09;明确异步处理方式 二、多线程线程池&#xff08;Thread Pool&#xff09; &#xff08;一&#xff09;工作原理 &#xff08;二&#xff09;直面优缺点和适用场景 1.需要快…...

Hadoop生态圈框架部署 伪集群版(一)- Linux操作系统安装及配置

文章目录 前言一、下载CentOS镜像1. 下载 二、创建虚拟机hadoop三、CentOS安装与配置1. 安装CentOS2. 配置虚拟网络及虚拟网卡2.1 配置虚拟网络2.2 配置虚拟网卡 3. 安装 SSH 远程连接工具 FinalShell3.1 简介3.2 下载和安装3.2.1 下载3.2.2 安装 3.3 查看动态ip地址3.4 使用Fi…...

Go的Gin比java的Springboot更加的开箱即用?

前言 隔壁组的云计算零零后女同事&#xff0c;后文简称 云女士 &#xff0c;非说 Go 的 Gin 框架比 Springboot 更加的开箱即用&#xff0c;我心想在 Java 里面 Springboot 已经打遍天下无敌手&#xff0c;这份底蕴岂是 Gin 能比。 但是云女士突出一个执拗&#xff0c;非我要…...

超短脉冲激光自聚焦效应

前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应&#xff0c;这是一种非线性光学现象&#xff0c;主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场&#xff0c;对材料产生非线性响应&#xff0c;可能…...

Java如何权衡是使用无序的数组还是有序的数组

在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

cf2117E

原题链接&#xff1a;https://codeforces.com/contest/2117/problem/E 题目背景&#xff1a; 给定两个数组a,b&#xff0c;可以执行多次以下操作&#xff1a;选择 i (1 < i < n - 1)&#xff0c;并设置 或&#xff0c;也可以在执行上述操作前执行一次删除任意 和 。求…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)

引言&#xff1a;为什么 Eureka 依然是存量系统的核心&#xff1f; 尽管 Nacos 等新注册中心崛起&#xff0c;但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制&#xff0c;是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

数据库分批入库

今天在工作中&#xff0c;遇到一个问题&#xff0c;就是分批查询的时候&#xff0c;由于批次过大导致出现了一些问题&#xff0c;一下是问题描述和解决方案&#xff1a; 示例&#xff1a; // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

《C++ 模板》

目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板&#xff0c;就像一个模具&#xff0c;里面可以将不同类型的材料做成一个形状&#xff0c;其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式&#xff1a;templa…...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用

文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么&#xff1f;1.1.2 感知机的工作原理 1.2 感知机的简单应用&#xff1a;基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...

深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏

一、引言 在深度学习中&#xff0c;我们训练出的神经网络往往非常庞大&#xff08;比如像 ResNet、YOLOv8、Vision Transformer&#xff09;&#xff0c;虽然精度很高&#xff0c;但“太重”了&#xff0c;运行起来很慢&#xff0c;占用内存大&#xff0c;不适合部署到手机、摄…...

算法打卡第18天

从中序与后序遍历序列构造二叉树 (力扣106题) 给定两个整数数组 inorder 和 postorder &#xff0c;其中 inorder 是二叉树的中序遍历&#xff0c; postorder 是同一棵树的后序遍历&#xff0c;请你构造并返回这颗 二叉树 。 示例 1: 输入&#xff1a;inorder [9,3,15,20,7…...