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

二分查找(带图详解)

优选算法系列


文章目录

  • 优选算法系列
  • 前言
  • 一、二分查找的思想
  • 二、算法使用
    • 小总结
  • 三、代码实现
  • 四、二分查找拓展
    • 4.1、查找第一次出现的target
      • 小总结
    • 4.2、target最后出现的位置
      • 小总结
  • 五、代码
  • 总结


前言

在这篇博客中,我会给大家分享二分查找及其扩展。

这是链接->Leetcode二分查找

我们先以常规二分查找来引入。

一、二分查找的思想

接下来的讲解,我们以这个例题为背景来展开叙述。链接已放置上方。

例:
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

示例 1:

输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4

示例 2:

输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1

提示:

1.你可以假设 nums 中的所有元素是不重复的。
2.n 将在 [1, 10000]之间。
3.nums 的每个元素都将在 [-9999, 9999]之间。

对于上面那题,如果我们使用暴力查找,当数据量比较大时查找效率时比较低的,而且还没有利用题中给除的,数组有序这个条件。
二分查找的思想是比较简单的,对于有序的数据,我们在查找某一值时:
首先选择数组中间的数字和需要查找的目标值比较
如果相等最好,就可以直接返回答案了
如果不相等:

1.如果中间的数字大于目标值,则中间数字向右的所有数字都大于目标值,全部排除
2.如果中间的数字小于目标值,则中间数字向左的所有数字都小于目标值,全部排除

二分法就是按照这种方式进行快速排除查找的。接下来我们对上面例题,使用二分思想进行画图解决。

二、算法使用

开始时设left为数据左指针,right为数据右指针,mid为中间值指针,target为目标值。
注:以下标作为指针进行计算。
mid=(left+right)/2,此时:
在这里插入图片描述
讲mid指针指向值于目标值比较,我们发现该值小于目标值,因为数组有序,所以mid指针左侧值都小于目标值,这时我们更行left指针,因为我们知道mid及其左侧值均已小于目标值所以:
left=mid+1;
再次进行查找
mid=(left+right)/2,此时:
在这里插入图片描述
再次与目标值进行比较,发现仍小于目标值,再次更新left指针
left=mid+1,再次查找
mid=(left+right)/2,此时:
在这里插入图片描述
将mid指向值与target进行比较,发现与目标值相等,循环结束。
这是查找是,目标值存在的情况下,那么如果目标值不存在呢?接下来我们继续分析.

对与上面相同过程我们就不赘述了。
在这里插入图片描述
我们接着将mid指向的值,与target比较,发现指向值大于目标值,此时我们可以确定,mid右侧值都大于目标值,这时我们就该更新右侧指针right。
right=mid-1;

这时我们就没有继续下去的必要了,因为left已经大于right了。那么当两个指针相等时我们还要继续判断吗?我们就制造除一个这样的场景看看吧。刚好你可以再梳理一遍过程。

首先进入循环:
mid=(left+right)/2,此时:
在这里插入图片描述
接着将mid指向值与target比较,发现小于target,舍弃mid及左边值。更新left
,left=mid+1,再次进入循环
mid=(left+right)/2,此时:

在这里插入图片描述
继续比较,更新left,left=mid+1,此时我们想要的场景就来了,更行left后:
在这里插入图片描述
这时就出现了当left=right时,我们是否继续循环的情况,结果很,明显吧,对于这两个指针指向的值,我们并不能将他排除掉,所以当然要再次进入循环了。

小总结

循环结束条件:左指针处在右指针的右边时(left>right)

细节问题:
1、在有的情况下我们使用mid=(left+right)/2来对指针取中时,如果left与right相加数值较大时(int类型存不下)可能会发生数据截断,这时我们往往采用mid=(right-left)/2+right,来代替上面的方式,至于为什么可以代替,大家举两个示例,计算一下就可以知道.
2、上一条提到的mid=(right-left)/2+left,也许你在其他地方见过mid=(right-left+1)/2+left这种写法,对于普通的二分查找,这两种写法并没有本质的区别,在下面我们会展开讲解

对于普通该念的二分查找,我们必须要求查找数据有序。

三、代码实现

int search(vector<int>& nums, int target) {int left=0,right=nums.size()-1;while(left<=right)//循环继续的条件{int mid=left+(right-left)/2;if(target>nums[mid])left=mid+1;//小于目标值跟新左指针else if(target<nums[mid])right=mid-1;//大于目标值更新右指针else return mid;//找到直接返回,结束循环}return -1;//未找到返回-1}

对于很多新手来说,大家往往被老师说的数组有序给限制了,大家思考一下,我们为什么可以一次排除一半的数据呢?这是因为我们可以从数据中选取一个值,这个值可以将数据分为,两部分(针对这里来说就是,我们可以时刻保证,左边比目标值小或右边比目标值大)而这个性质被称为二段性。其实对于可以将数据分为两部分的,我们都可以考虑,它是否可以使用二分来解决。

四、二分查找拓展

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

接下来我们以这个题为背景来讲解

例:
给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]

示例 2:

输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]

示例 3:

输入:nums = [], target = 0
输出:[-1,-1]

提示:

0 <= nums.length <= 105
-109 <= nums[i] <= 109
nums 是一个非递减数组
-109 <= target <= 109

这一题,我们使用上面讲的方法,是无法解决的,因为其中含有重复值,数组并非绝对有序,这时可能有人想,利用上面方法找到目标值后,将指针向前移动来找第一次出现的位置,向后移动来找第二次出现的位置不就可以了吗?但是这样的想法,当碰到一些特殊场景(如:目标值为3,待查找空间全为3)就相当于将数据都遍历一遍,这时我们的查找效率就大打折扣了。

上面我们提到,可以利用二分查找的原因并不是数组严格有序,是因为我们可以从中找取一个值将数据分为两部分,即数据具二段性。
在这里插入图片描述
对于上面这组数据,在查target第一次出现的位置时,我们可以利用第一个target,将左边变为小于target的,右边变为大于等于target的。同理查找最后出现的位置,我们利用target将左边变为小于等于target的,右边变为大于target的。这样说是很难理解的,接下来我们直接画图解题。

4.1、查找第一次出现的target

细节问题会在下面统一总结

首先进入循环
mid=left+(right-left)/2,此时:

在这里插入图片描述
比较mid指向的值,于target的关系,发现mid小于target,此时我们所选取的值就可以将数据分为两部分:

不合法区域:一定不含目标值的区域
合法区域:目标值存在区域

这种情况我们肯定是将左侧区域排除的,那么我们怎么更新左指针呢?当然是让他跳出不和法区域了,即:
left=mid+1,此时:
在这里插入图片描述
这样第一轮循环算是结束了,但这时候我们并没有找到目标中,所以再次进入循环:
mid=left+(right-left)/2,此时:

在这里插入图片描述
将mid指向的值,与target比较发现相等,此时我们并不知道mid指向的位置是否为第一个target的值,
但是我们可以确定mid右侧(不包含mid)的值都大于等于target
在这里插入图片描述

所以这时候我们虽然不知到它是否为第一个值,但我们可以确定它右侧一定不包含第一次出现的target,这时我们就可以更新right:
right=mid,为是什么这样更新呢,因为我们无法将mid排除,此时:

在这里插入图片描述
再次进入循环,mid=left+(right-left)/2,此时:
在这里插入图片描述
比较target与mid指向的值,发现相等,继续更新right,right=mid此时:
在这里插入图片描述
这是我们还要进入循环吗?很显然不需要了,当left==right是我们就可以跳出循环,判断他们所指向值是否,等于target,如果相等,我们就找到了,那么如果,待查找值不存在呢?它的结束条件又是什么呢?接下来我们继续看:

为了大家好理解,这次我们以上面的逻辑从头开始。

首先进入循环
mid=left+(right-left)/2,此时:
在这里插入图片描述

将mid指向值与target比较发现小于target,更新left ,left=mid=1,此时:
在这里插入图片描述

再次进入循环,mid=left=(right-left)/2,此时:
在这里插入图片描述

比较…更新left,left=mid+1,此时:

在这里插入图片描述
再次进入mid=left+(right-left)/2,此时:
在这里插入图片描述
比较…更新…相等了就不要再继续了
服啦我画麻了!!!!气死了…啊啊啊。

小总结

1.结束条件:left<right,当left=right时我们只需结束循环,并在循环外进行判断即可。
2.这里不可使用mid=left+(right-left+1)/2,具体原因当我们使用这个对mid进行计算时,有可能进入死循环如:
在这里插入图片描述
当我们次进入循环时,计算的mid仍然不变,这时我们就会更新right,right=mid…再次循环。
这样就造成了死循环问题。

4.2、target最后出现的位置

因为我们要查找最后一次出现的位置,所以要保证左边为小于等于target的,右边为大于target的这样如果存在则left最终指向的就是要查找值,这里大家也继续在过程中体会吧

进入循环,mid=left+(right-left+1)/2(这里和上面有点区别,后面分析),此时:
在这里插入图片描述
将mid指向值与target比较发现,mid指向值满足小于等于target,left=mid,此时:

在这里插入图片描述
再次进入循环,mid=left+(right-left+1)/2,此时:
在这里插入图片描述
继续比较,此时mid指向值仍然小于等于target,再次更新,left=mid,此时:

在这里插入图片描述
进入循环,mid=left+(right-left+1)/2,此时:
在这里插入图片描述
将mid指向值于target比较,发下大于target,更新right,right=mid-1,此时:
在这里插入图片描述

小总结

1.这里不可以使用mid=left+(right-left)/2来跟新mid,不然会造成死循环,大家将两次对比记忆。

五、代码

vector<int> searchRange(vector<int>& nums, int target) {vector<int>v={-1,-1};int left=0,right=nums.size()-1;//初始指针while(left<right){int mid=left+(right-left)/2;if(nums[mid]<target)left=mid+1;else right=mid;}if(left<nums.size()&&nums[left]==target) v[0]=left;left=0;right=nums.size()-1;//重置指针while(left<right){int mid=left+(right-left+1)/2;if(nums[mid]<=target) left=mid;else right=mid-1;}if(right<nums.size()&&nums[right]==target) v[1]=right;return v;}

总结

这里并没有将所以可能遇到的情况罗列出来,入果大家想尝试可以用下面三种模拟:
1.有节果
2.全大于
3.全小于

下面几题,可以使用二分思想来解答:
1.x的平方根
2.搜索插入位置
3.山脉数组的峰顶索引
4.寻找峰值

相关文章:

二分查找(带图详解)

优选算法系列 文章目录 优选算法系列前言一、二分查找的思想二、算法使用小总结 三、代码实现四、二分查找拓展4.1、查找第一次出现的target小总结 4.2、target最后出现的位置小总结 五、代码总结 前言 在这篇博客中&#xff0c;我会给大家分享二分查找及其扩展。 这是链接-&…...

【Git】:标签管理

目录 理解标签 创建标签 操作标签 理解标签 标签的作用 标记版本&#xff1a;标签 tag &#xff0c;可以简单的理解为是对某次 commit 的⼀个标识&#xff0c;相当于起了⼀个别名。例如&#xff0c;在项目发布某个版本的时候&#xff0c;针对最后⼀次 commit 起⼀个 v1.0 这样…...

物品识别 树莓派 5 YOLO v5 v8 v10 11 计算机视觉

0. 要实现的效果 让树莓派可以识别身边的一些物品&#xff0c;比如电脑&#xff0c;鼠标&#xff0c;键盘&#xff0c;杯子&#xff0c;行李箱&#xff0c;双肩包&#xff0c;床&#xff0c;椅子等 1. 硬件设备 树莓派 5 raspberrypi.com/products/raspberry-pi-5/树莓派官方摄…...

单片机软件工程师前景分析

单片机软件工程师的前景在2024年看起来是积极的。随着物联网&#xff08;IoT&#xff09;、自动化、智能设备等领域的快速发展&#xff0c;对于能够开发基于单片机&#xff08;MCU&#xff09;如STM32、ARM、51等嵌入式系统的软件工程师需求持续增长。这些工程师负责设计和实现…...

在Java中几种常用数据压缩算法的实现及其优劣势

在Java中几种常用数据压缩算法的实现及其优劣势 背景&#xff1a;项目需要引入Redis作为缓存组件&#xff0c;需要考虑到Redis的内存占用&#xff08;机器内存越大&#xff0c;成本越高&#xff09;&#xff0c;因此需要引入数据压缩。 1、介绍 数据压缩是计算机领域中一项重要…...

Word——如何打出 符号中的 1、2、3等带圆圈的序号

一、方式1 1.1&#xff1a;点击 插入-符号 1.2&#xff1a;字体 选择 Wingdings 或者 Wingdings 2 二、方式2 带1的圈&#xff1a;输入 2460&#xff0c;然后按 AItX 带2的圈&#xff1a;输入 2461&#xff0c;然后按 AItX 带3的圈&#xff1a;输入 2462&#xff0c;然后按 …...

操作系统之进程与线程

进程 定义&#xff1a; 进程是具有独立功能的程序关于某个数据集合上的一次运行活动&#xff0c;是系统进行资源分配和调度的独立单位。 组成&#xff1a; 包括程序代码、程序处理的数据、程序计数器、一组寄存器的值以及系统资源&#xff08;如打开的文件&#xff09;等。 …...

代码随想录算法训练营打卡第35天:背包问题

前言 zaccheo打卡代码随想录第35天 由于这段时间工作太忙了&#xff08;加上我的懒病犯了&#xff09;导致迟打卡了好几天555555.。。。 今天的主要是动态规划中的背包问题&#xff0c;这个真的是蛮难理解的&#xff0c;我把我自己强行按在椅子上半个小时一点一点的看卡哥文章…...

【MySQL】数据库 Navicat 可视化工具与 MySQL 命令行基本操作

&#x1f4af; 欢迎光临清流君的博客小天地&#xff0c;这里是我分享技术与心得的温馨角落 &#x1f4af; &#x1f525; 个人主页:【清流君】&#x1f525; &#x1f4da; 系列专栏: 运动控制 | 决策规划 | 机器人数值优化 &#x1f4da; &#x1f31f;始终保持好奇心&…...

vscode(一)安装(ubuntu20.04)

1、更新软件包列表 sudo apt update2、安装依赖包 sudo apt install software-properties-common apt-transport-https wget3、导入Microsoft GPG密钥 wget -q https://packages.microsoft.com/keys/microsoft.asc -O- | sudo apt-key add -4、向系统添加VSCode存储库 sudo…...

利用永恒之蓝对win7进行键盘记录

打开kali中的msfconsole 找到永恒之蓝&#xff0c;设置靶机ip&#xff0c;后可以exploit&#xff0c;也可以run 连接成功 查看进程&#xff0c;选择监听靶机win7上的cmd.exe进程 当前进程不是1484&#xff0c;需要迁移到1484 cmd.exe&#xff0c;进程迁移 键盘监听&#xff0c;…...

万字长文解读深度学习——dVAE(DALL·E的核心部件)

&#x1f33a;历史文章列表&#x1f33a; 深度学习——优化算法、激活函数、归一化、正则化 深度学习——权重初始化、评估指标、梯度消失和梯度爆炸 深度学习——前向传播与反向传播、神经网络&#xff08;前馈神经网络与反馈神经网络&#xff09;、常见算法概要汇总 万字长…...

RL仿真库pybullet

1. 介绍 PyBullet是一个基于Bullet Physics引擎的物理仿真Python接口&#xff0c;主要用于机器人仿真模拟。 1.1 主要特点 提供大量预设的机器人模型&#xff0c;例如URDF(统一机器人描述格式)、SDF、MJCF 格式。适用于训练和评估强化学习算法&#xff0c;提供了大量的强化学…...

file_get_contents函数导致网站卡死响应超时

宝塔控制面板系统下运行包含file_get_contents函数的php文件时候&#xff0c;发生以下报错&#xff1a; PHP Warning: file_get_contents():php_network_getaddresses: getaddrinfo failed: 解决方法&#xff1a; 一&#xff1a;需要检查请求的远程主机是否在本机的/etc/host…...

如何使用C#与SQL Server数据库进行交互

一.创建数据库 用VS 创建数据库的步骤&#xff1a; 1.打开vs&#xff0c;创建一个新项目&#xff0c;分别在搜素框中选择C#、Windows、桌面&#xff0c;然后选择Windows窗体应用(.NET Framework) 2.打开“视图-服务器资源管理器”&#xff0c;右键单击“数据连接”&#xff0…...

#渗透测试#红蓝对抗#SRC漏洞挖掘# Yakit(5)进阶模式-MITM中间人代理与劫持(上)

免责声明 本教程仅为合法的教学目的而准备&#xff0c;严禁用于任何形式的违法犯罪活动及其他商业行为&#xff0c;在使用本教程前&#xff0c;您应确保该行为符合当地的法律法规&#xff0c;继续阅读即表示您需自行承担所有操作的后果&#xff0c;如有异议&#xff0c;请立即停…...

vue3 项目搭建-9-通过 router 在跳转页面时传参

第一步&#xff0c;在跳转链接处挂载方法&#xff0c;将要传输的数据传入&#xff1a; <a href"#" click.prevent"goToArticle(obj.id)" class"click"><h1>{{obj.title}}</h1><p>作者&#xff1a;{{obj.author}}</p&…...

Java、python标识符命名规范

Java 包名所有字母一律小写。例如cn.com.test类名和接口名每个单词的首字母都要大写。例如ArrayList、Iterator常量名所有字母都大写&#xff0c;单词之间用下划线连接&#xff0c;例如&#xff1a;DAY_OF_MONTH变量名和方法名的第一个单词首字母小写&#xff0c;从第二个单词…...

高效职场人

文章目录 1.时间效能 ABCD2.高效员工的习惯之 自我掌控的秘诀3.学会做主4.学会互赢5.学会沟通、学会聆听6.学会可持续发展&#xff1a;四个方面更新自我(1)更新身体(2)更新精神(3)更新智力(4)更新人际情感 1.时间效能 ABCD 时间四象限&#xff1a; A类任务&#xff1a;重要且紧…...

深入探索现代 IT 技术:从云计算到人工智能的全面解析

目录 1. 云计算&#xff1a;重塑 IT 基础设施 2. 大数据&#xff1a;挖掘信息的价值 3. 物联网&#xff08;IoT&#xff09;&#xff1a;连接物理世界 4. 区块链&#xff1a;重塑信任机制 5. 人工智能&#xff08;AI&#xff09;&#xff1a;智能未来的驱动力 结语 在当今…...

【AI学习】苹果技术报告《Apple Intelligence Foundation Language Models》

文章地址&#xff1a;https://machinelearning.apple.com/papers/apple_intelligence_foundation_language_models.pdf 这篇文章介绍了苹果公司开发的基础语言模型&#xff08;Apple Foundation Language Models&#xff0c;简称AFM&#xff09;&#xff0c;这些模型旨在为苹果…...

深度相机获取实时图像总结

问题详情&#xff1a;之前一直把曝光调整到50000&#xff0c;画面一直很流畅&#xff0c;知道领导要求将曝光改成500000时整个程序卡死了 问题解决&#xff1a; 首先怀疑是帧率太低的原因&#xff0c;控制变量后发现不是帧率的问题&#xff0c;看着代码很迷茫&#xff0c;领导…...

Nginx限流实践-limit_req和limit_conn的使用说明

注意&#xff1a; 本文内容于 2024-12-07 19:38:40 创建&#xff0c;可能不会在此平台上进行更新。如果您希望查看最新版本或更多相关内容&#xff0c;请访问原文地址&#xff1a;Nginx限流实践。感谢您的关注与支持&#xff01; 一、限流 之前我有记录通过CentOS7定时任务实…...

Unity在运行状态下,当物体Mesh网格发生变化时,如何让MeshCollider碰撞体也随之实时同步变化?

旧版源代码地址&#xff1a;https://download.csdn.net/download/qq_41603955/90087225?spm1001.2014.3001.5501 旧版效果展示&#xff1a; 新版加上MeshCollider后的效果&#xff1a; 注意&#xff1a;在Unity中&#xff0c;当你动态地更改物体的Mesh时&#xff0c;通常期望…...

记一次由docker容器使得服务器cpu占满密码和密钥无法访问bug

Bug场景&#xff1a; 前几天在服务器上部署了一个免费影视网站&#xff0c;这个应用需要四个容器&#xff0c;同时之前的建站软件workpress也是使用docker部署的&#xff0c;也使用了三个容器。在使用workpress之前&#xff0c;我将影视软件的容器全部停止。 再使用workpress…...

前端TS基础

文章目录 一、类型1、定义类型2、any、unknown、never3、基础类型4、联合类型5、交叉类型6、type、typeof7、作用域 二、数据结构1、数组2、元组3、函数类型4、对象类型 三、接口四、泛型五、enum六、断言七、工具1、模块2、namespace3、装饰器4、declare5、运算符6、映射类型7…...

前端面经每日一题day06

Cookie有什么字段 Name&#xff1a;cookie的唯一标识符 Value&#xff1a;与Name对应&#xff0c;存储Cookie的信息 Domain&#xff1a;可以访问cookie的域名 Path&#xff1a;可以访问cookie的路径 Expires/Max-Age&#xff1a;超时时间 Size&#xff1a;cookie大小 Ht…...

SOC,SOH含义区别及计算公式

SOC&#xff0c;SOH含义区别及计算公式 两者结合使用&#xff0c;有助于实现更精确的电池管理&#xff0c;延长电池的使用寿命&#xff0c;并确保电池的高效、安全运行。 1. SOC&#xff08;State of Charge&#xff0c;荷电状态&#xff09;2. SOH&#xff08;State of Health…...

阿里云轻量应用服务器开放端口,图文教程分享

阿里云轻量应用服务器如何开放端口&#xff1f;在轻量服务器管理控制台的防火墙中添加规则即可开通端口&#xff0c;开通80端口就填80&#xff0c;开通443就填443端口&#xff0c;开通3306端口就填3306。阿里云百科网aliyunbaike.com整理阿里云轻量应用服务器端口号开通图文教程…...

嵌入式里的“移植”概念

这里因为最近一年看到公司某项目很多代码上有直接硬件的操作&#xff0c;这里有感而发&#xff0c;介绍移植的概念。 一、硬件 先上一个图&#xff1a; 举个例子&#xff0c;大学里应该都买过开发板&#xff0c;例如st的&#xff0c;这里三个层次&#xff0c; 内核&#xff…...