二分查找及其变种

一、概念

二分查找算法(Binary Search Algorithm)是一种在有序数组中查找特定元素的高效搜索方法。

其基本思想是将目标值与数组中间的元素进行比较,如果目标值等于中间元素,则查找成功;如果目标值小于中间元素,则在数组左半部分继续查找;如果目标值大于中间元素,则在数组右半部分继续查找。这个过程将不断重复,直到找到目标值或搜索范围为空为止。

二、实现步骤

2.1 初始化: 确定搜索范围的左右边界,通常用两个指针表示,一个指向数组的起始位置(left,索引0),另一个指向数组的结束位置(right,数组长度减1)。

2.2 循环条件: 当左指针小于等于右指针时,继续查找。

2.3 计算中间索引: 计算当前搜索范围的中间位置 mid,通常使用 (left + right) / 2 来避免整数溢出。

口诀:奇数二分取中间 偶数二分取中间靠左;

2.4 比较中间元素: 比较 array[mid] 与目标值 target:

  • 如果 array[mid] 等于 target,则查找成功,返回 mid。
  • 如果 array[mid] 大于 target,则在左半部分继续查找,更新右指针 right = mid - 1。
  • 如果 array[mid] 小于 target,则在右半部分继续查找,更新左指针 left = mid + 1。

2.5 结束循环: 如果左指针大于右指针,表示搜索范围为空,目标值不在数组中,返回一个表示未找到的值,如 -1。

三、代码实现

public class BinarySearch {public int binarySearch(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) {return mid; // 找到目标值,返回索引} else if (nums[mid] < target) {left = mid + 1; // 在右半部分查找} else {right = mid - 1; // 在左半部分查找}}return -1; // 未找到目标值,返回-1}
}

注意事项:

 1. 如果left 和 right比较大的话,两者之和就有可能会溢出。改进的方法是将 mid 的计算方式写成 left +(right-left )/2。更进一步,如果要将性能优化到极致的话,我们可以将这里的除以 2 操作转化成位运算 left +((right-left )>>1)。因为相比除法运算来说,计算机处理位运算要快得多。

2. while  (left < = right),注意括号内为 left <= right ,而不是 left < right ,如果我们设置条件为 left  <  right 则当我们执行到最后一步时,则我们的 left 和 right 重叠时,则会跳出循环,返回 -1,区间内不存在该元素,但是不是这样的,我们的 left 和 right 此时指向的就是我们的目标元素 ,但是此时 left = right 跳出循环

3. left = mid + 1,right = mid - 1 而不是 left = mid 和 right = mid。当我们的target 元素为 16 时,然后我们此时 left = 7 ,right = 8,mid = left + (right - left) = 7 + (8-7) = 7,那如果设置 left = mid 的话,则会进入死循环,mid  值一直为7 。

四、算法变种

4.1 

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]。

示例 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]

从最原始的二分查找代码中分析, nums[mid] == target 时则返回,nums[mid] < target 时则移动左指针,在右区间进行查找, nums[mid]  >  target 时则移动右指针,在左区间内进行查找。

那么我们思考一下,如果此时我们的 nums[mid] = target ,但是我们不能确定 mid 是否为该目标数的左边界,所以此时我们不可以返回下标。

 

此时 mid = 4 ,nums[mid] = 5,但是此时的 mid 指向的并不是第一个 5,所以我们需要继续查找 ,因为我们要找的是数的下边界,所以我们需要在 mid 的值的左区间继续寻找 5 ,那应该怎么做呢?

我们只需在target <= nums[mid] 时,让 right = mid - 1即可,这样我们就可以继续在 mid 的左区间继续找 5 。

解决方案:

将小于和等于合并在一起处理,当 target <= nums[mid] 时,我们都移动右指针,也就是 right = mid -1,还有一个需要注意的就是,我们计算下边界时最后的返回值为 left ,当上图结束循环时,left = 3,right = 2,返回 left 刚好时我们的下边界。

计算上边界时算是和计算上边界时条件相反,

计算下边界时,当  target <= nums[mid]  时,right = mid -1;target > nums[mid] 时,left = mid + 1;

计算上边界时,当  target < nums[mid] 时,right = mid -1; target >= nums[mid] 时 left = mid + 1;刚好和计算下边界时条件相反,返回right。

public class Solution {public static int[] searchRange(int[] nums,int target){int upper = upperBound(nums, target);int low = lowerBound(nums, target);//不存在情况if (upper < low){return new int[]{-1,-1};}return new int[]{low,upper};}//计算下边界static int lowerBound(int[] nums,int target){int left = 0, right = nums.length - 1;while (left <= right){//计算midint mid = left + ((right - left) >> 1);if (target <= nums[mid]){//当目标值小于等于nums[mid]时,继续在左区间检索,找到第一个数right = mid -1;}else if (target > nums[mid]){//目标值大于nums[mid]时,则在右区间继续检索,找到第一个等于目标值的数left = mid + 1;}}return left;}//计算上边界static int upperBound(int[] nums,int target){int left = 0,right = nums.length - 1;while (left <= right){int mid = left + ((right - left) >> 1);if (target >= nums[mid]){left = mid + 1;}else if (target < nums[mid]){right = mid - 1;}}return right;}
}

 

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

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

相关文章

第十五章 Qt的QGraphics View绘图框架详解

目录 一、视图、场景、图形项 1、Graphics View 绘图架构 1.1、图形项 GraphicsItem 1.2、场景 QGraphicsScene 1.3、视图 QGraphicsView 2、图形项、场景、视图的坐标系 3、Graphics View 基础练习 二、使用自定义视图处理鼠标事件 1、添加自定义视图类 2、使用自定…

ScaleCache: A Scalable Page Cache for Multiple Solid-State Drives——论文泛读

EuroSys 2024 Paper 论文阅读笔记整理 问题 高性能存储设备&#xff0c;如具有GB/s级I/O带宽的NVMe SSD&#xff0c;已被广泛应用于企业服务器中。对于处理大量数据&#xff0c;在RAID配置中使用多个SSD很有吸引力&#xff0c;这可以提高I/O性能、可靠性和容量。尽管多个SSD为…

全面教程:在Ubuntu上快速部署ZeroTier,实现Windows与VSCode的局域网无缝访问

文章目录 1 背景介绍2 Windows上的操作3 Ubuntu上的操作4 连接 1 背景介绍 在现代工作环境中&#xff0c;远程访问公司内网的Ubuntu主机对于开发者来说是一项基本需求。然而&#xff0c;由于内网的限制&#xff0c;传统的远程控制软件如向日葵和todesk往往无法满足这一需求。作…

华硕电脑格式化后电脑会怎样?数据怎么恢复

在数字化时代&#xff0c;电脑已经成为我们日常生活和工作中不可或缺的设备。然而&#xff0c;在使用电脑的过程中&#xff0c;有时我们会遇到需要格式化硬盘的情况。对于华硕电脑用户而言&#xff0c;了解格式化后的影响以及如何恢复磁盘数据至关重要。本文将详细探讨华硕电脑…

《昇思25天学习打卡营第9天|保存与加载》

文章目录 今日所学&#xff1a;一、构建与准备二、保存和加载模型权重三、保存和加载MindIR总结 今日所学&#xff1a; 在上一章节主要学习了如何调整超参数以进行网络模型训练。在这一过程中&#xff0c;我们通常会想要保存一些中间或最终的结果&#xff0c;以便进行后续的模…

Swift Core Data 分阶段迁移

文章目录 前言什么是分阶段迁移&#xff1f;提供一些背景信息创建迁移管理器设置使用 Core Data 栈。总结 前言 在这之前&#xff0c;我发布了一篇文章&#xff0c;在其中解释了如何使用映射模型和自定义迁移策略执行复杂的 Core Data 迁移。虽然这种方法性能良好且运行良好&a…

【Linux进阶】文件和目录的默认权限与隐藏权限

1.文件默认权限&#xff1a;umask OK&#xff0c;那么现在我们知道如何建立或是改变一个目录或文件的属性了&#xff0c;不过&#xff0c;你知道当你建立一个新的文件或目录时&#xff0c;它的默认权限会是什么吗&#xff1f; 呵呵&#xff0c;那就与umask这个玩意儿有关了&…

Vue85-Vuex的求和案例

一、需求 二、开发 2-1、index.js中vuex的代码 注意&#xff1a; 书写格式&#xff1a;actions中的函数名用小写&#xff01;mutations中的函数名&#xff0c;用大写。 注意&#xff1a; 2-2、组件count.vue中的代码 2-3、代码优化 三、actions中的context参数 此写法的后…

Python基于PyQt5和卷积神经网络分类模型(ResNet50分类算法)实现生活垃圾分类系统GUI界面项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 在当今社会&#xff0c;随着人们对环境保护意识的增强以及科技的快速发展&#xff0c;智能化的垃圾分类…

EasyBoss ERP移动端上线数据分析模块,随时查Shopee/TikTok本土店数据

前段时间&#xff0c;EasyBoss ERP出了个超酷炫的数字大屏功能&#xff0c;广受好评。 但是也有老板说&#xff0c;电脑端看数据不够方便啊&#xff0c;你们EasyBoss有本事上个手机就能看数据的功能啊&#xff01; 说干就干&#xff0c;直接满足你们的需求&#xff01; 于是在…

day02-统计数据

numpy统计学 1.求平均值[数组名.mean()/np.mean(数组名)] m1 np.arange(20).reshape((4,5))m1.mean() #9.5若想要求某一维的平均值&#xff0c;设置axis参数&#xff0c;多维数组元素指定&#xff1a; axis 0&#xff0c;将从上往下计算。axis 1&#xff0c;将从左往右计算…

44 mysql batch insert 的实现

前言 我们这里 来探讨一下 insert into $fields values ($values1), ($values2), ($values3); 的相关实现, 然后 大致来看一下 为什么 他能这么快 按照 我的思考, 应该里里面有 批量插入才对, 但是 调试结果 发现令我有一些意外 呵呵 果然 只有调试才是唯一的真理 相比于 …

Linux的Socket开发概述

套接字&#xff08;socket&#xff09;是 Linux 下的一种进程间通信机制&#xff08;socket IPC&#xff09;&#xff0c;在前面的内容中已经给大家提到过&#xff0c;使用 socket IPC 可以使得在不同主机上的应用程序之间进行通信&#xff08;网络通信&#xff09;&#xff0c…

MATLAB和Python发那科ABB库卡史陶比尔工业机器人模拟示教框架

&#x1f3af;要点 &#x1f3af;模拟工业机器人 | &#x1f3af;可视化机器人DH 参数&#xff0c;机器人三维视图 | &#x1f3af;绘制观察运动时关节坐标位置、速度和加速度 | &#x1f3af;绘制每个关节处的扭矩和力 | &#x1f3af;图形界面示教机器人 | &#x1f3af;工业…

Qt入门小项目 | WPS tab页面(无边框窗口综合应用)

文章目录 一、手写代码实现WPS tab页面 一、手写代码实现WPS tab页面 实现类似WPS tab效果&#xff0c;具体包含&#xff1a; 自定义标题栏&#xff1a;最大、最小、关闭在QTabWidget的tab上增加控件在QTabWidget的tab上右键菜单可拖拽移动可拉伸窗口双击标题栏在最大与正常间…

Objection 对命令的批量操作

假定现在需要对好多不同的类进行批量hook&#xff0c;逐个hook非常繁琐&#xff0c;那么可以要将这些hook的类放到一个文件里&#xff0c;并且在这些类的前面加上hook命令&#xff0c;内容如下 使用如下命令执行该文件中的命令 objection -g 测试 explore -c d:/hookData/toHoo…

昇思25天学习打卡营第13天|ResNet50图像分类

1. 学习内容复盘 图像分类是最基础的计算机视觉应用&#xff0c;属于有监督学习类别&#xff0c;如给定一张图像(猫、狗、飞机、汽车等等)&#xff0c;判断图像所属的类别。本章将介绍使用ResNet50网络对CIFAR-10数据集进行分类。 ResNet网络介绍 ResNet50网络是2015年由微软…

传承与创新,想让认字更简单?就来看《米小圈动画汉字》吧!

汉字&#xff0c;作为中华文化的精髓和根基&#xff0c;自古以来便承载着中华民族的思想与记忆。在现代社会&#xff0c;随着文化多样性的崛起和科技进步的推动&#xff0c;汉字的教育也更加的多元化&#xff0c;《米小圈动画汉字》作为一项全新的教育资源&#xff0c;不仅致力…

【python基础】—calendar模块

文章目录 前言一、calendar模块方法1.firstweekday()2.setfirstweekday(firstweekday)3.isleap(year)4.leapdays(y1, y2)5.weekday(year, month, day)6.monthrange(year, month)7.weekheader(n)8.monthcalendar(year, month)9.prmonth(theyear, themonth, w0, l0)10.prcal(year…

【硬核科普】存算一体化系统(Processing-in-Memory, PIM)深入解析

文章目录 0. 前言1. 提出背景1.1 存储墙1.2 功耗墙 2. 架构方案2.1 核心特征2.2 技术实现2.2.1 电流模式2.2.2 电压模式2.2.3 模式选择 2.3 PIM方案优势 3. 应用场景4. 典型产品4.1 鸿图H304.2 三星HBM-PIM 5. 存算一体化缺点6. 总结 0. 前言 按照国际惯例&#xff0c;首先声明…