二分查找算法:朴素二分+左右边界二分力扣实战应用

目录:

1、二分查找算法简介

2、算法原理及时间复杂度分析

2.1 朴素二分算法

3.2 查找左右边界的二分算法

3.2.1 查找左边界

3.2.2 查找右边界

3.3 时间复杂度分析

3、二分查找算法模版

3.1 朴素二分模版

3.2 查找左右边界的二分模版

4、算法应用【leetcode】

4.1 题一:搜素插入位置

4.1.1 思路分析

4.1.2 算法代码

4.2 题二:x的平方根

4.2.1 思路分析

4.2.2 算法代码

4.3 题三:山峰数组的峰顶索引

4.3.1 思路分析

4.3.2 算法代码

4.4 题四:寻找峰值

4.4.1 思路分析

4.4.2 算法代码

4.5 题五:寻找旋转排序数组中的最小值

4.5.1 思路分析

4.5.2 算法代码

4.6 题六:点名 (原:剑指Offer:0~n-1 中缺失的数字 )

 4.6.1 思路分析

4.6.2 算法代码


1、二分查找算法简介

算法,是一种思想,并不是固定的模式,我们可以使用一种算法思想解决多种问题。

二分查找算法,是一个细节最多、最恶心、最容易写出死循环的一个算法,但是当我们熟练掌握后它就可以变成一个最简单的算法,利用它,仅仅使用十几行代码就可以解决掉一个难题,所以在算法学习的路途中,二分查找算法的学习是极为重要且必不可少的。

二分法查找算法的使用条件:数据具有“二段性”。(并非数据有序)

注意:并不是只有数据有序的情况下才可以使用二分查找算法,只要数据具有二段性,即使数据乱序,也可以使用二分查找算法!!!

2、算法原理及时间复杂度分析

这里通过例题为大家讲解算法原理。

2.1 朴素二分算法

. - 力扣(LeetCode)

使用二分查找算法的关键点是:数据具有“二段性”。

因为数组为升序排列,所以target左边的数据均<target,target右边的数据均>target,故可将数据分为“二段”,具有二段性,可以使用二分查找算法。

定义left和right指针分别指向数组0下标处和nums.length-1处,以及定义他们的中间位置mid,将nums[mid]和target比较,

  1. nums[mid] < target ---> left = mid+1;
  2. nums[mid] > target ---> right = mid-1;
  3. nums[mid] == target ---> 返回结果;

这样一次比较即可过滤掉一半数据,大大提高了查找效率。、

注意:

  • 循环条件为:left <= right
  • 为防止数据溢出,更新mid的方式为:mid = left + (right - left) / 2;或者mid = left + (right - left + 1) / 2;

class Solution {public int search(int[] nums, int target) {int left = 0;int right = nums.length-1;while (left <= right) {int mid = left+(right-left)/2;if(nums[mid] < target) {left = mid+1;} else if (nums[mid] > target) {right = mid-1;}else {return mid;}}return -1;}
}

注意:朴素二分算法因为太过简单,所以基本不会考察,重点是下方的边界二分查找算法的思想。


3.2 查找左右边界的二分算法

. - 力扣(LeetCode)

因为数组为非递减排序,也就是说数据要么相等,要么递增,而我们要找的就是相等数据target的开始和结束位置。

也就是说,我们要找到target的左边界位置和右边界位置,而target的左边的数据小于target,右边的数据大于target,数据同样具有二段性,可以使用二分查找算法。

同样定义left和right指针,定义mid指向他们的中间位置。

3.2.1 查找左边界

  1. 循环条件为:left < right,left == right时就是最终结果,结束循环
  2. nums[mid] < target ---> left = mid+1;//mid的位置肯定不为左边界,所以left = mid+1
  3. num[mid] >= target ---> right = mid;//mid的位置可能就是左边界,所以right=mid
  4. 更新mid:mid = left + (right - left) / 2;

3.2.2 查找右边界

  1. 循环条件为:left < right,left == right时就是最终结果,结束循环
  2. nums[mid] <= target ---> left = mid;//mid的位置可能就是右边界,所以left=mid
  3. nums[mid] > target ---> right = mid-1;//mid的位置肯定不为右边界,所以right= mid-1
  4. 更新mid:mid = left + (right - left + 1) / 2;因为更新right或left时,有-1操作,所以这里更新mid要+1(技巧,记忆即可)

class Solution {public int[] searchRange(int[] nums, int target) {int left = 0;int right = nums.length - 1;int[] arr = new int[]{-1,-1};//nums为空数组if (nums.length == 0) return arr;//查找左边界while (left < right) {int mid = left + (right - left) / 2;if (nums[mid] < target) {left = mid + 1;}else {right = mid;}}//数组中不存在targetif (nums[left] != target) return arr;arr[0] = left;left = 0;right = nums.length - 1;//查找右边界while (left < right) {int mid = left + (right - left + 1) / 2;if (nums[mid] <= target) {left = mid;}else {right = mid - 1;}}arr[1] = left;return arr;}
}

注意:

  • 朴素二分算法的循环条件是:left <= right;因为要查找的数据可能就在left和right重叠的位置处。
  • 边界二分算法的循环条件是:left < right;因为当left == right时,就是最终结果。
  • 为避免数据溢出:mid = left + (right - left) / 2 或者 mid = left + (right - left + 1) / 2,朴素算法+1与否都可以,但是在找边界的二分算法中,若更新left或者right时,有-1出现,则更新的mid要+1。

3.3 时间复杂度分析

第一次二分查找剩下n/2个数据,第二次二分查找剩下n/4个数据,第三次二分查找升序n/8个数据,直至最后一次(第x次)二分查找剩下1个数据(此时查找成功),则n/2^{x} == 1,计算得x == logN,因为x就是循环执行的次数,故二分查找算法时间复杂度为:O(logN)

大家可能觉得O(logN)对于O(N)的提升不是很大,其实并不是这样,举个例子:

假设存储了2^{32}个数据,若用O(N)的算法去查找某一个数据,即遍历所有数据,那么最多需要查找2^{32}=4,294,967,296次;而使用O(logN)的算法,则最多查找32次就可以查找成功。

综上所述,O(logN)相对于O(N)的提升非常的大,故二分查找算法是一个极为高效的算法,每次都能排除掉一半的数据,从而快速定位到目标位置。

3、二分查找算法模版

对于算法的模版,一定不要死记硬背,要理解后再记忆,这样才可以在不同的题目中灵活使用该算法。

3.1 朴素二分模版

朴素二分模版是最简单的二分模版,因为简单,所以也很少考察。

注意:朴素模版中的循环条件为: left <= right

//朴素二分模版while (left <= right) {int mid = left + (right - left) / 2;//避免数据溢出//int mid = left + (right - left + 1) / 2;在朴素二分中,加不加1均可if(....) {left = mid+1;} else if (....) {right = mid-1;}else {return ....;}}

3.2 查找左右边界的二分模版

注意:边界模版中的循环条件为: left < right


4、算法应用【leetcode】

4.1 题一:搜素插入位置

. - 力扣(LeetCode)

4.1.1 思路分析

分析数据,可以发现target要插入的位置就是第一个比target大的数据的位置,而这个位置左侧的均小于target,右侧的数据均大于target,故具有二段性,可以使用二分查找算法。

而我们的目的就是:找到第一个大于target的数据的位置,返回这个位置的下标即可。

需要注意一个边界情况:当target比所以数据都大时,也就是说target要插入数组的末尾,需要特殊处理。

4.1.2 算法代码

class Solution {public int searchInsert(int[] nums, int target) {int left = 0;int right = nums.length-1;while (left < right) {int mid = left + (right - left) / 2;if (nums[mid] < target) {left = mid + 1;}else {right = mid;}}return target > nums[left] ? left + 1 : left;}
}

4.2 题二:x的平方根

. - 力扣(LeetCode)

4.2.1 思路分析

分析数据,因为目标数据的平方是 小于或等于 x的,所以目标数据及其左侧数据(包括目标数据)的平方均小于等于x,右侧数据的平方均大于x。故具有二段性,可使用二分查找算法解决该题。

4.2.2 算法代码

class Solution {public int mySqrt(int x) {long left = 0;long right = x;while (left < right) {//mid*mid 可能超出范围,定义为long长整型long mid = left + (right - left + 1) / 2;if (mid * mid <= x) {left = mid;}else {right = mid - 1;}}return (int)left;}
}

4.3 题三:山峰数组的峰顶索引

. - 力扣(LeetCode)

4.3.1 思路分析

由题意可知,数组一定为山峰,故峰顶左侧的数据一定小于峰顶值,峰顶右侧的数据一定大于峰顶值,故数据具有二段性,可使用二分查找算法。且题目已说明使用O(logN)的算法,故必须使用二分查找算法。

算法思想很简单:

  1. 若arr[mid] > arr[mid-1],则峰顶一定在mid右侧或峰顶就为mid;//left = mid
  2. 若arr[mid] < arr[mid-1],则峰顶一定在mid左侧;//right = mid-1

4.3.2 算法代码

class Solution {public int peakIndexInMountainArray(int[] arr) {int left = 0;int right = arr.length - 1;while (left < right) {int mid = left + (right - left + 1) / 2;if(arr[mid] > arr[mid - 1]) {left = mid;}else {right = mid - 1;}}return left;}
}

4.4 题四:寻找峰值

. - 力扣(LeetCode)

4.4.1 思路分析

该题思路与上一题山峰数组的解题思路是一模一样的,只不过可能存在多个山峰,也就是说数组是完全无序的,所以,二分查找算法的使用并不局限于有序数组,只要数据具有二段性,就可以使用二分查找算法。

  1. 将中间值arr[mid]与arr[mid-1]比较,若arr[mid] < arr[mid-1],说明在左侧一定有峰值,而右侧是不确定的,可能有也可能没有,这样就可以过滤掉右侧数据,在左侧数据继续寻找山峰;
  2. 同样,若arr[mid] > arr[mid-1],说明右侧一定有山峰,而左侧不确定,过滤左侧数据,故发现数据具有二段性,能够使用二分查找算法。
  3. 因为 nums[-1] = nums[n] = -∞,所以即使数组为递增或递减序列时,也能够正确查找到峰值的位置。

4.4.2 算法代码

class Solution {public int findPeakElement(int[] arr) {int left = 0;int right = arr.length - 1;while (left < right) {int mid = left + (right - left + 1) / 2;if(arr[mid] > arr[mid - 1]) {left = mid;}else {right = mid - 1;}}return left;}
}

4.5 题五:寻找旋转排序数组中的最小值

. - 力扣(LeetCode)

4.5.1 思路分析

因为数组原来是升序排列,所以经过旋转,数值大的元素就移动到了数组的前面部分。

所以:

  1. 未经过旋转的元素必然小于等于数组的最后一个元素。
  2. 而经过旋转的元素必然大于数组的最后一个元素。
  3. 故数据具有二段性,可以使用二分查找算法。

因为我们是和数组的最后一个元素比较,所以即使在数组完全旋转的特殊情况下也可以得到正确结果。 

而如果是和数组的第一个元素比较的话,在特殊情况时,还需特殊处理,这种解法留给大家,可以锻炼大家的代码能力以及加强对二分查找算法的理解。

 4.5.2 算法代码

class Solution {public int findMin(int[] nums) {int left = 0;int right = nums.length - 1;while (left < right) {int mid = left + (right - left) / 2;if (nums[mid] <= nums[nums.length-1]) {right = mid;}else {left = mid + 1;}}return nums[left];}
}

4.6 题六:点名 (原:剑指Offer:0~n-1 中缺失的数字 )

 . - 力扣(LeetCode)

 4.6.1 思路分析

  1. 在数组中,缺失的数字前的数据其值与其下标是相对应的。
  2. 缺失的数字后的数据其值都比其下标大1,故数据具有二段性,可以使用二分查找算法。
  3. 第一个数值与下标不对应的数据的位置,就是缺失的数据。

特殊情况:缺的是最后一个数据时,数组中的所有数据与其下标均对应,此时需要特殊处理。

4.6.2 算法代码

class Solution {public int takeAttendance(int[] records) {int left = 0;int right = records.length - 1;while (left < right) {int mid = left + (right - left) / 2;if (records[mid] > mid) {right = mid;}else {left = mid + 1;}}return records[left] == left ? left + 1 : left;}
}

END

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/409072.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

考研数学暑期进度大调查,你掉队了吗?

现在已经8月&#xff0c;马上快9月份了&#xff0c;你的数学进度学到哪里啦&#xff1f; 我可不是“进度哥“&#xff0c;也不会营造焦虑&#xff0c;其实对于进度这个事情&#xff0c;我一直觉得是一个伪命题&#xff0c;因为很多同学一直鼓吹进度多快&#xff0c;结果最后考…

合宙LuatOS开发板使用说明——Air700ECQ

EVB-Air700ECQ-IO 开发板是合宙通信推出的基于 Air700ECQ 模组所开发的&#xff0c;包含电 源&#xff0c; SIM 卡&#xff0c;USB &#xff0c;天 线&#xff0c; 全 IO 引 出的最 小硬 件系 统。以 方便 用户 在设 计前期 对 Air700ECQ 模块进行性能评估&#xff0c;功能调试…

Hadoop集群运维管理

Hadoop集群运维管理 一、Hadoop 集群进程管理1.1 NameNode 守护进程管理1.2 DataNode 守护进程管理1.3 ResourceManager 守护进程管理1.4 NodeManager 守护进程管理 二、Hadoop 集群运维技巧2.1 查看日志2.2 清理临时文件2.3 定期执行负载均衡2.4 文件系统检查2.5 元数据备份 三…

Maven的一些相关知识【重修】《包括私服搭建!》

mvnrepository.com Maven 下载jar包的位置&#xff01; 【该部分有教程】 这是什么nb代码投稿视频-这是什么nb代码视频分享-哔哩哔哩视频 MAVEN 的私服搭建&#xff1a; https://zhuanlan.zhihu.com/p/520107316 2、maven私服搭建及应用&#xff08;下&#xff09;_哔哩…

SQL手工注入漏洞测试(PostgreSQL数据库)

判断注入点 and 12 判断回显点 order 不用 4 页面正常 order by 5 页面异常&#xff0c;得出只存在四个字段 测试回显位置 and 12 union select null,null,null,null and 12 union select null,null,null,null and 12 union select null,null,null,null and 12 union select…

如何在不格式化的情况下解锁 Android 智能手机密码

如果您忘记密码&#xff0c;您的 Android 移动设备将锁定您。发生这种情况时&#xff0c;通常可以通过恢复出厂设置来重新获得对设备的访问权限。可悲的是&#xff0c;这将导致所有数据丢失。下面列出的是解锁锁定的Android 手机而不会丢失任何个人数据的有效方法。 Android 手…

Open3D 近似点体素滤波(36)

Open3D 近似点体素滤波(36) 一、算法介绍二、算法实现1.代码2.效果一、算法介绍 这个算法也是体素滤波, 它保留的点是近似点,也就是新的点,原始点云中对应位置是不存在这些点的。其他的看着类似,下面是代码,滤波抽稀结果 二、算法实现 1.代码 代码如下(示例): …

Long Short-Term Memory

这篇论文总结的太抽象了&#xff0c;只是翻译了一遍。 &#xff08;我太笨了&#xff0c;如果把这个当我的入门读物&#xff0c;我觉着会把我折磨坏&#xff09; 递归神经网络的一个重要优点是它们在映射输入和输出序列时使用上下文信息的能力。不幸的是&#xff0c;对于标准的…

Chainlit接入FastGpt接口完美对接,实现全新的用户聊天界面

前言 由于fastgpt只提供了一个分享用的网页应用&#xff0c;网页访问地址没法自定义&#xff0c;虽然可以接入NextWeb/ChatGPT web等开源应用。但是如果我们想直接给客户应用&#xff0c;还需要客户去设置配置&#xff0c;里面还有很多我们不想展示给客户的东西怎么办&#xf…

[数据集][目标检测]街灯路灯检测数据集VOC+YOLO格式1893张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;1893 标注数量(xml文件个数)&#xff1a;1893 标注数量(txt文件个数)&#xff1a;1893 标注…

数据结构(Java实现):链表习题

文章目录 1. 题目列表及链接2. 题目解析及代码2.1 删除链表中等于给定值 val 的所有节点2.2 反转一个单链表2.3 给定一个带有头结点 head 的非空单链表&#xff0c;返回链表的中间结点。如果有两个中间结点&#xff0c;则返回第二个中间结点2.4 输入一个链表&#xff0c;输出该…

iTransformer时序模型改进——基于SENet和TCN的倒置Transformer,性能暴涨

1数据集介绍 ETT(电变压器温度)&#xff1a;由两个小时级数据集&#xff08;ETTh&#xff09;和两个 15 分钟级数据集&#xff08;ETTm&#xff09;组成。它们中的每一个都包含 2016 年 7 月至 2018 年 7 月的七种石油和电力变压器的负载特征。 数据集链接&#xff1a; https…

深度学习入门-第4章-神经网络的学习

学习就是从训练数据中自动获取最优权重参数的过程。引入损失函数这一指标&#xff0c;学习的目的是找出使损失函数达到最小的权重参数。使用函数斜率的梯度法来找这个最小值。 人工智能有两派&#xff0c;一派认为实现人工智能必须用逻辑和符号系统&#xff0c;自顶向下看问题…

Sass实现网页背景主题切换

Sass 实现网页背景主题切换 前言准备工作一、 简单的两种主题黑白切换1.定义主题2. 添加主题切换功能3. 修改 data-theme 属性 二、多种主题切换1. 定义主题2. 动态生成 CSS 变量1.遍历列表2.遍历映射3.高级用法 3. 设置默认主题4. 切换功能HTML 三、多种主题多种样式切换1. 定…

Java数组的定义与使用

目录 1. 数组的基本概念 1.1为什么要使用数组 1.2 什么是数组 1.3 数组的创建及初始化 1.3.1 数组的创建 1.3.2 数组的初始化 1. 动态初始化 2. 静态初始化 1.4 数组的使用 1.4.1 数组中元素访问 1.4.2 遍历数组 2. 数组是引用类型 2.1 基本类型变量与引用类型变量…

【C++从小白到大牛】C++智能指针的使用、原理和分类

目录 1、我们为什么需要智能指针&#xff1f; 2、内存泄露 2.1 什么是内存泄漏&#xff0c;内存泄漏的危害 2.2如何避免内存泄漏 总结一下: 3.智能指针的使用及原理 3.1 RAII 3.2关于深拷贝和浅拷贝更深层次的理解&#xff1a; 3.3 std::auto_ptr 3.4 std::unique_pt…

Springboot里集成Mybatis-plus、ClickHouse

&#x1f339;作者主页&#xff1a;青花锁 &#x1f339;简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java微服务架构公号作者&#x1f604; &#x1f339;简历模板、学习资料、面试题库、技术互助 &#x1f339;文末获取联系方式 &#x1f4dd; Springboot里集成Mybati…

Overleaf参考文献由 BibTex 转 \bibitem 格式

目录 Overleaf参考文献由 BibTex 转 \bibitem 格式一、获取引用论文的BibTex二、编写引用论文对应的bib文件三、编写生成bibitem的tex文件四、转化bibitem格式 参考资料 Overleaf参考文献由 BibTex 转 \bibitem 格式 一、获取引用论文的BibTex 搜索论文引用点击BibTex 跳转出…

怎样快速搭建 Linux 虚拟机呢?(vagrant 篇)

作为一名Coder&#xff08;程序员或码农&#xff09;&#xff0c;供职于中小型互联网公司&#xff0c;而你恰恰偏向于服务端&#xff0c;那么&#xff0c;产品部署在生产环境的艰巨任务&#xff0c;便毫无疑问的落在你身上了。 只有大厂&#xff08;大型互联网&#xff09;企业…

Ps:首选项 - 界面

Ps菜单&#xff1a;编辑/首选项 Edit/Preferences 快捷键&#xff1a;Ctrl K Photoshop 首选项中的“界面” Interface选项卡可以定制 Photoshop 的界面外观和行为&#xff0c;从而创建一个最适合自己工作习惯和需求的工作环境。这些设置有助于提高工作效率&#xff0c;减轻眼…