【算法篇】二分查找算法:基础篇

news2025/5/29 14:54:38

题目链接:
34.在排序数组中查找元素的第一个和最后一个位置

题目描述:
给你一个按照非递减顺序排列的整数数组 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

当left=right的时候,就是最终结果,无需判断

当我们在题目中发现这个数组有二段性的时候,就可以使用二分查找算法了
我们不能同时查找开始位置和结束位置,所以我们就先开始去查找第一个位置;
也就是开始位置:

第一步:查找区间的左端点:

根据目标值target把整个数组划分为两个部分:
把数组分为两个部分,

  1. 左边全部都是小于t
  2. 右边全部都是大于等于t

定义一个left下标指向数组的左端点,定义一个right下标指向数字的右端点,也就是指向数组的最后一个位置

接着定义一个mid中间值,把这个中间值设置为x,

分情况讨论:
1: x < t:
当这个中间值小于t的时候,落于左边的区间中,也就是没有t的区间中,此时就需要让left向右移动了,让left = mid + 1,去新的区间【left,right】,中寻找新的结果:

2: x >= t:
此时把大于和等于合在一起,不要分情况讨论了,注意,我们目前要寻找的是左边的端点,所以当中间值mid >= t的时候,就让right向左移动去寻找左端点,但是不是让rihgt = mid - 1了,万一这个mid 刚好是左端点,此时就应该让right = mid即可,更新完毕之后,就会去新的区间【left,right】中寻找了

难点是接下来的细节处理:
第一个细节:
循环的条件:我们什么时候去执行这个循环

这个循环条件有两个可以选择:

  1. left <= right
  2. left < right

此时我们只能选择第二种循环条件,因为第一种会陷入死循环:
这个循环条件中,我们不能让left = right

我们分情况来讨论:

第一种情况:
在这个【left,right】区间中含有这个taget:
在这里插入图片描述

第二种情况:
在这个【left,right】区间中,全部都是大于target的:
在这里插入图片描述

第三种情况:
这个【left,right】区间中所有的值全部都小于target:
在这里插入图片描述

上面我们分了三种情况去讲解为什么不需要在left = right的时候去再次执行这个循环,
下面去讲解一下为什么不可以在循环条件中加上left = right
因为一旦加上了这个left =right,就会导致死循环

在刚刚的第一种情况中会陷入死循环:
在这里插入图片描述

所以这就是为什么在循环条件中不可以加入left = right的原因

第二个细节:
求中点的操作:
这个中点mid应该如何去求出来:
之前我们去计算中点mid有两种方式:

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

第一种求中点的位置是求的靠左的位置
第二种求中点的位置是求的是靠右的位置

当使用第二种情况去求出右端点的时候会导致死循环:

当最终的时候,只剩下两个元素的时候:
在这里插入图片描述

所以我们求中点不可以使用第二种方法,只能去使用第一种方法了

所以以上就是求出左端点的全部过程了

第二步:查找区间的右端点

继续根据目标值把数据划分为两个部分:

  1. 左边的部分:小于等于target : 移动左指针,left = mid
  2. 右边的部分:大于target:移动右指针:right = mid - 1

之前讲述过,求中点的计算方式一共有两种:

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

但是中点mid的计算方式不同:
![[Pasted image 20250514160645.png]]

所以在查找区间的右端点的时候,计算中点的方式就是:
mid = left + (right - left +1) / 2

下面就是这道题目的具体代码了:

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int[] ret = new int[2];
        ret[0] = ret[1] = -1;
        if(nums.length == 0){
            return ret;
        }
        int left = 0,right = nums.length-1;
        while(left <right){
            int mid = left+(right - left) / 2;
            if(nums[mid] < target){
                left = mid+1;
            }else{
                right = mid;
            }
        }
        if(nums[left] != target){
            return ret;
        }else{
            ret[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;
            }
        }
        if(nums[left] != target){
            return ret;
        }else{
            ret[1] = right;
        }
        return ret;
    }
}

下面去按照8步归纳法:

第一步:用自己的话,描述一下眼前的这个问题,它是一个怎么样的问题?

给定一个数组,和目标值,在这个数组中找出目标值的开始下标和结束下标。

第二步:这个问题有哪些特征,能让我们去判断,它属于这一类的问题?
这个数组是可以划分为两段的,分开来看,先去找左端点,就会将这个数组划分为小于target的区间和大于 等于target的区间,就可以根据这个二段性去使用二分查找方法解决问题了

第三步:想要解决这类问题,切入点啥,即第一步我们要从哪里开始?
切入点,先去查找左端点,方法:根据这个target将数组划分为两段,第一段是小于target的区间,第二段是大于等于目标值的区间,然后使用二分找出这个左端点

第四步:解决这个问题的流程是怎么样的?这里说的流程是指,这道题可以分成12345…步,只要按照12345这个顺序做下去,我们就能解决这个问题。

  1. 先去找左端点,根据target将数组分为小于target的区间和大于等于target的区间,使用二分查找方法找出左端点,定义出左右下标,如果这个mid的值是小于target的,那就是位于小于target的区间,此时就让left = mid+1 、, 如果这个mid的值是大于等于target的,那么这个mid的值是位于大于target的区间的,那就让right = mid 即可
  2. 当left == right的时候,就说明此时这个left == right值就是目标值,这个left/right就是左端点了
  3. 根据第一步,即可求出左端点,如果没有左端点,那就返回-1
  4. 下面来求出右端点:方法一样,定义出左右下标,使用二分,根据这个target将这个数组划分为两个区间,一个是小于等于target的区间,一个是大于target的区间,使用二分查找方法,如果mid的值 <= target,那就是位于<=target的区间,那就让left = mid ,如果mid的值>target,那就让right =mid -1
  5. 最后遍历下来,当left= right的时候,循环结束,此时left = right的位置就是右端点了
  6. 最后使用一个两个长度的数组将这个左端点和右端点的下标放进去返回即可

第五步:在流程的12345步中,每一步的目标是什么(就是要求到些什么)?每一步需要用到哪些知识

  1. 当数组可以被划分为二段的时候,就可以使用二分查找方法
  2. 如果左区间是 <target,右区间是>= target的话,那么此时如果mid的值是位于左区间的,就left = mid+1,如果mid的值是位于右区间的,那就right = mid
  3. 此时这个中点的求法:mid = left + (right- left) / 2
  4. 当去求出右端点的时候,区间划分是< target的区间和 >= target的区间,此时这个中点的求法:mid = left + (right - left +1) /2 ;
  5. 如果最后没有找到,那就直接返回一个只有两个元素的数组即可
  6. 这个结果数组初始化为两个元素,里面是两个-1,如果一开始这个参数中的目标数组中就是一个元素都没有,那就直接返回这个只有两个-1的结果数组

第六步:要思考在运用这些知识和技巧的时候,有些需要注意的地方。

错误的区间划分方式

第一步:求出左端点的过程中,一定是要先划分出小于target区间和大于等于target区间的,不可以反回来,也就是在求左端点的时候一定不可以划分出小于等于target的区间和大于target的区间,这样是错误的方法,因为如果在求左端点的时候,按照这个错误的方法划分出了区间,会使得:

原因如下:
这种划分区间方式会导致左端点查找逻辑失效。主要问题在于当nums[mid] == target时,你将 mid 赋值给 left,这可能会跳过真正的左端点。

在找左端点的过程中,按照这种错误的思路:
假设我们有数组[5,7,7,8,8,10],目标值target=8。正确的左端点索引是 3。

  1. nums[mid] <= target时,令left = mid
  2. nums[mid] > target时,令right = mid-1

在第一次二分查找时:

  • 初始区间[0,5],mid=2,nums[2]=7,满足nums[mid] <= target,于是left=2
  • 新区间[2,5],mid=3,nums[3]=8,满足nums[mid] <= target,于是left=3
  • 新区间[3,5],mid=4,nums[4]=8,满足nums[mid] <= target,于是left=4
  • 新区间[4,5],mid=4,nums[4]=8,满足nums[mid] <= target,于是left=4
  • 此后陷入死循环,无法找到左端点 3

区间划分方式无法保证左端点被包含在搜索区间内。当nums[mid] == target时,真正的左端点可能在 mid 的左侧,但你的代码却将搜索范围向右移动,导致左端点被跳过。

第七步:要解决这个问题,最终的目标是啥?也就是说,我最终要求出的是啥?

最终的目标是求出目标数组中的含有目标值的数组下标

第八步:重新对上面的第一步至第七步,进行回顾和揣摩(包括问题类型,特征,切入点,解决过程,1234567…步,每步需要用的哪些知识方法)

(2)AI+8步归纳结合
在模仿完题目后,先自己用8步归纳法对错题进行归纳,然后让deepseek,按照8步归纳法的原则,对题目进行总结归纳,你再把自己总结归纳出的东西,跟AI对比,看看有哪些地方可以改进。

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

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

相关文章

重磅发布 | 复旦533页《大规模语言模型:从理论到实践(第2版)》(免费下载)

在人工智能浪潮席卷全球的今天&#xff0c;大语言模型正以前所未有的速度推动着科技进步和产业变革。从 ChatGPT 到各类行业应用&#xff0c;LLM 不仅重塑了人机交互的方式&#xff0c;更成为推动学术研究与产业创新的关键技术。 面对这一飞速演进的技术体系&#xff0c;如何系…

智能体赋能效率,企业知识库沉淀价值:UMI企业智脑的双轮驱动!

智能体企业知识库&#xff1a;UMI企业智脑的核心功能与价值 在人工智能技术飞速发展的今天&#xff0c;企业智能化转型已经成为不可逆转的趋势。作为企业级AI智能体开发平台的佼佼者&#xff0c;优秘智能推出的UMI企业智脑&#xff0c;以其强大的智能体开发能力和全面的企业知…

vue项目 build时@vue-office/docx报错

我在打包vue项目时&#xff0c; 开始用的npm run build和cnpm run build&#xff0c;总是提示 vue-office/docx 错误&#xff0c;尝试过用cnpm重新安装node_modules几次都没用。类似下面的提示一直有。 Error: [commonjs--resolver] Failed to resolve entry for package "…

#RabbitMQ# 消息队列入门

目录 一 MQ技术选型 1 运行rabbitmq 2 基本介绍 3 快速入门 1 交换机负责路由消息给队列 2 数据隔离 二 Java客户端 1 快速入门 2 WorkQueue 3 FanOut交换机 4 Direct交换机 5 Topic交换机 *6 声明队列交换机 1 在配置类当中声明 2 使用注解的方式指定 7 消息转…

EPT(Efficient Prompt Tuning)方法,旨在解决提示调优(Prompt Tuning)中效率与准确性平衡和跨任务一致性的问题

EPT(Efficient Prompt Tuning)方法,旨在解决提示调优(Prompt Tuning)中效率与准确性平衡和跨任务一致性的问题 一、核心原理:分解提示与多空间投影 1. 提示分解:用低秩矩阵压缩长提示 传统问题: 长提示(如100个token)精度高但训练慢,短提示(如20个token)速度快但…

云原生安全核心:云安全责任共担模型(Shared Responsibility Model)详解

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 1. 基础概念 什么是云安全责任共担模型&#xff1f; 云安全责任共担模型&#xff08;Shared Responsibility Model, SRM&#xff09;是云服务提供商&…

go并发与锁之sync.Mutex入门

sync.Mutex 原理&#xff1a;一个共享的变量&#xff0c;哪个线程握到了&#xff0c;哪个线程可以执行代码 功能&#xff1a;一个性能不错的悲观锁&#xff0c;使用方式和Java的ReentrantLock很像&#xff0c;就是手动Lock&#xff0c;手动UnLock。 使用例子&#xff1a; v…

[Java恶补day8] 3. 无重复字符的最长子串

给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长 子串 的长度。 示例 1: 输入: s “abcabcbb” 输出: 3 解释: 因为无重复字符的最长子串是 “abc”&#xff0c;所以其长度为 3。 示例 2: 输入: s “bbbbb” 输出: 1 解释: 因为无重复字符的最长子串是 “…

LabVIEW教学用开发平台

一、培训目标 基础编程&#xff1a;掌握 LabVIEW 数据类型、程序结构、子 VI 设计与调试技巧。 硬件通信&#xff1a;精通 RS-232/485、TCP/IP、Modbus、PLC 等工业通信协议及实现。 高级设计模式&#xff1a;熟练运用状态机、生产者 - 消费者模式构建复杂测控系统。 项目实…

Package Size Comparison – 6 Leads

Package Size Comparison 6 LeadsTSOP SOT SM SMT SOT23 SC-74 SC-59 SC-88 SOT363 US6 UMT6 SC-70 SOT563 ES EMT SC-75-6

python打卡day38

Dataset和DataLoader 知识点回顾&#xff1a; Dataset类的__getitem__和__len__方法&#xff08;本质是python的特殊方法&#xff09;Dataloader类minist手写数据集的了解 作业&#xff1a;了解下cifar数据集&#xff0c;尝试获取其中一张图片 在遇到大规模数据集时&#xff0c…

vLLM 核心技术 PagedAttention 原理详解

本文是 vLLM 系列文章的第二篇&#xff0c;介绍 vLLM 核心技术 PagedAttention 的设计理念与实现机制。 vLLM PagedAttention 论文精读视频可以在这里观看&#xff1a;https://www.bilibili.com/video/BV1GWjjzfE1b 往期文章&#xff1a; vLLM 快速部署指南 1 引言&#xf…

《软件工程》第 2 章 -UML 与 RUP 统一过程

在软件工程领域&#xff0c;UML&#xff08;统一建模语言&#xff09;与 RUP&#xff08;统一过程&#xff09;是进行面向对象软件开发的重要工具和方法。接下来&#xff0c;我们将深入探讨第 2 章的内容&#xff0c;通过案例和代码&#xff0c;帮助大家理解和掌握相关知识。 …

(转)Docker与K8S的区别

1 定义角度 Docker是一种开放源码的应用容器引擎&#xff0c;允许开发人员将其应用和依赖包打包成可移植的容器/镜像中&#xff1b;然后&#xff0c;发布到任何流行的 Linux 或 Windows 机器上&#xff0c;也能实现虚拟化。该容器完全使用沙箱机制&#xff0c;彼此之间没有任何…

商用密码 vs 普通密码:安全加密的核心区别

商用密码 vs 普通密码&#xff1a;安全加密的核心区别 一. 引言&#xff1a;密码的世界二. 什么是普通密码&#xff1f;三. 什么是商用密码&#xff1f;四. 普通密码 vs 商用密码&#xff1a;核心区别五. 选择合适的密码方案六. 结语 前言 肝文不易&#xff0c;点个免费的赞和…

MYSQL中的分库分表及产生的分布式问题

分库分表是分布式数据库架构中常用的优化手段&#xff0c;用于解决单库单表数据量过大、性能瓶颈等问题。其核心思想是将数据分散到多个数据库&#xff08;分库&#xff09;或多个表&#xff08;分表&#xff09;中&#xff0c;以提升系统的吞吐量、查询性能和可扩展性。 一&am…

投影机三色光源和单色光源实拍对比:一场视觉体验的终极较量

一、光源技术&#xff1a;从 “单色模拟” 到 “三色原生” 的进化 &#xff08;一&#xff09;单色光源&#xff1a;白光的 “色彩魔术” 单色光源投影机采用单一白光作为基础光源&#xff0c;通过LCD上出现色彩呈现颜色。这种技术路线的优势在于成本可控&#xff0c;早期被广…

电子电气架构 --- 下一代汽车电子电气架构中的连接性

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 钝感力的“钝”,不是木讷、迟钝,而是直面困境的韧劲和耐力,是面对外界噪音的通透淡然。 生活中有两种人,一种人格外在意别人的眼光;另一种人无论…

手写Tomcat(一)

一、Tomcat简介 Tomcat 服务器是一个免费的开放源代码的Web应用服务器&#xff0c;属于轻量级应用服务器&#xff0c;在中小型系统和并发访问用户不是很多的场合下被普遍使用&#xff0c;是开发和调试JSP 程序的首选。 1.1 Tomcat基本架构 Servlet接口文件中定义的方法有以下…

【机器学习基础】机器学习入门核心算法:支持向量机(SVM)

机器学习入门核心算法&#xff1a;支持向量机&#xff08;SVM&#xff09; 一、算法逻辑1.1 基本概念1.2 核心思想线性可分情况 二、算法原理与数学推导2.1 原始优化问题2.2 拉格朗日对偶2.3 对偶问题2.4 核函数技巧2.5 软间隔与松弛变量 三、模型评估3.1 评估指标3.2 交叉验证…