LeetCode 34:在排序数组中查找元素的首尾位置——Java 两次二分查找详解
一、题目描述给定一个按照非递减顺序排列的整数数组nums以及一个目标值target要求找出目标值在数组中的开始位置和结束位置。如果数组中不存在target返回[-1,-1]。题目要求算法的时间复杂度必须为O(log n)。例如输入nums [5,7,7,8,8,10], target 8 输出[3,4]数字8出现了两次第一次出现的位置是下标3最后一次出现的位置是下标4。如果目标值为6数组中不存在该数字则返回[-1,-1]数组已经有序并且时间复杂度要求为O(log n)因此这道题应该使用二分查找。问题在于普通二分查找只能找到目标值的某一个位置如何进一步找到它的左右边界二、普通二分查找为什么不够标准二分查找在发现nums[mid] target时会立即返回midif (nums[mid] target) { return mid; }但是当数组中存在多个相同元素时这个mid不一定是第一个位置也不一定是最后一个位置。例如nums [5,7,7,8,8,10] target 8二分查找可能先找到下标4但我们还不能确定下标3是否也是8。同理即使第一次找到的是下标3也不能确定它右侧是否还有目标值。因此本题找到target后不能立即返回而是应该先记录当前找到的下标根据要查找的边界继续向左或向右搜索直到搜索区间为空最后一次记录的位置就是对应边界。三、整体思路执行两次二分查找目标值的开始位置和结束位置是两个不同的问题可以分别执行一次二分查找第一次查找左边界即目标值第一次出现的位置第二次查找右边界即目标值最后一次出现的位置。两个搜索过程的大部分逻辑完全相同只有在命中目标值后的搜索方向不同。因此可以编写一个findMost方法并通过布尔参数isLeft区分搜索目标findMost(nums, target, true); // 查找左边界 findMost(nums, target, false); // 查找右边界最终将两次搜索的结果组合起来return new int[] { findMost(nums, target, true), findMost(nums, target, false) };如果目标值不存在两次搜索都会返回初始值-1自然得到[-1,-1]。四、tempIndex为什么必不可少在二分查找中定义一个临时变量int tempIndex -1;每当发现nums[mid] target时就把当前下标记录下来tempIndex mid;之所以只记录而不立即返回是因为当前的mid只是一个候选边界真正的左边界可能还在左侧真正的右边界也可能还在右侧。如果后续搜索又找到了更靠近目标方向的相同元素就再次更新tempIndex。当循环结束时tempIndex保存的就是最终边界。如果整个搜索过程中一次都没有找到targettempIndex会保持为-1。五、如何查找左边界查找左边界时遇到nums[mid] target说明当前下标可能是第一个位置但它的左边仍可能存在相同元素。因此先记录当前位置再继续搜索左半部分tempIndex mid; right mid - 1;例如nums [5,7,7,8,8,10] target 8如果当前找到下标4先把4记录下来然后将右边界移动到3继续检查左侧。若之后发现下标3也是8就用3更新tempIndex。循环结束后得到左边界3。可以把查找左边界的规则记为命中后记录答案并继续向左搜索。六、如何查找右边界查找右边界的逻辑与左边界相反。当nums[mid] target时当前下标可能是最后一个位置但右侧仍可能存在相同元素。因此先记录当前位置再继续搜索右半部分tempIndex mid; left mid 1;如果当前先找到下标3就将3记录下来然后继续搜索它的右侧。后续找到下标4时再将tempIndex更新为4。循环结束后得到右边界4。对应的记忆规则是命中后记录答案并继续向右搜索。七、未命中时如何更新区间除了命中目标值后的特殊处理其余情况与标准二分查找完全相同。如果中间元素小于目标值目标值只能出现在右侧if (nums[mid] target) { left mid 1; }如果中间元素大于目标值目标值只能出现在左侧else if (nums[mid] target) { right mid - 1; }只有在nums[mid] target时才需要根据isLeft决定继续搜索的方向。八、完整 Java 代码下面的代码严格使用同一个findMost方法完成左右边界查找class Solution { public int[] searchRange(int[] nums, int target) { return new int[] { findMost(nums, target, true), findMost(nums, target, false) }; } // isLeft 为 true 时查找左边界否则查找右边界 private int findMost(int[] nums, int target, boolean isLeft) { int left 0; int right nums.length - 1; int tempIndex -1; while (left right) { int mid left ((right - left) 1); if (nums[mid] target) { left mid 1; } else if (nums[mid] target) { right mid - 1; } else { // 先记录当前命中的位置 tempIndex mid; if (isLeft) { // 查找左边界继续向左搜索 right mid - 1; } else { // 查找右边界继续向右搜索 left mid 1; } } } return tempIndex; } }这里使用下面的方式计算中间下标int mid left ((right - left) 1);它与(left right) / 2的作用相同但可以避免left right过大时出现整数溢出。九、示例推演以nums [5,7,7,8,8,10]、target 8为例。1. 查找左边界初始区间为[0,5]mid 2nums[2] 7 8 left 3搜索区间变为[3,5]mid 4nums[4] 8 tempIndex 4 right 3由于查找左边界命中后继续向左。此时区间为[3,3]mid 3nums[3] 8 tempIndex 3 right 2循环结束左边界为3。2. 查找右边界前两步同样会找到下标4tempIndex 4 left 5因为查找右边界命中后继续向右。接下来nums[5] 10 8搜索结束右边界为4。最终返回[3,4]十、复杂度分析查找左边界和右边界分别执行一次二分查找每次的时间复杂度都是O(log n)。两次相加仍然是O(log n)算法只使用了left、right、mid和tempIndex等变量没有创建与数组长度相关的额外空间因此空间复杂度为O(1)十一、常见错误1. 找到目标值后立即返回这样只能得到目标值的任意一个位置无法保证它是左边界或右边界。2. 命中后没有保存当前位置继续搜索可能会导致最终区间为空因此必须先使用tempIndex保存当前候选答案。3. 左右边界的搜索方向写反查找左边界时应执行right mid - 1查找右边界时应执行left mid 1。4. 使用线性扫描寻找边界先二分找到目标值再向左右逐个扫描最坏情况下需要遍历整个数组时间复杂度会退化为O(n)不符合题目要求。5. 使用left right配合闭区间边界本文采用闭区间[left,right]因此循环条件必须是left right。如果混用不同二分模板容易漏掉只剩一个元素的情况。十二、总结这道题是在标准二分查找基础上增加了“边界搜索”。由于数组中可能出现多个连续的目标值命中目标值后不能立即返回而要记录当前位置并继续向对应方向搜索。为了避免编写两套重复代码可以通过isLeft参数复用一个findMost方法isLeft为true时继续向左收缩寻找第一次出现的位置为false时继续向右收缩寻找最后一次出现的位置。

相关新闻

Windows系统重装全流程指南:从备份到驱动安装的完整实践

Windows系统重装全流程指南:从备份到驱动安装的完整实践

1. 从“系统崩溃”到“焕然一新”:为什么你需要这篇指南如果你正在搜索“电脑重装系统”,大概率正面临一个令人头疼的局面:也许是用了三五年的老电脑,开机比蜗牛还慢,弹窗广告比正事还多;也许是手贱更新了某…

2026/10/9 8:48:51 阅读更多 →
一键捕获完整网页:告别拼接烦恼的Chrome截图终极方案

一键捕获完整网页:告别拼接烦恼的Chrome截图终极方案

一键捕获完整网页:告别拼接烦恼的Chrome截图终极方案 【免费下载链接】full-page-screen-capture-chrome-extension One-click full page screen captures in Google Chrome 项目地址: https://gitcode.com/gh_mirrors/fu/full-page-screen-capture-chrome-extens…

2026/9/28 15:04:35 阅读更多 →
ComfyUI-VideoHelperSuite终极教程:3步解决AI视频创作格式难题

ComfyUI-VideoHelperSuite终极教程:3步解决AI视频创作格式难题

ComfyUI-VideoHelperSuite终极教程:3步解决AI视频创作格式难题 【免费下载链接】ComfyUI-VideoHelperSuite Nodes related to video workflows 项目地址: https://gitcode.com/gh_mirrors/co/ComfyUI-VideoHelperSuite 你是否在为AI视频创作中的格式兼容性问…

2026/9/30 15:53:21 阅读更多 →

最新新闻

考研数据结构复试高频考点、手写代码与面试应对策略

考研数据结构复试高频考点、手写代码与面试应对策略

初试成绩出来之后,很多同学会陷入一种“不知道该干什么”的状态。刷题吧,好像复试不考这么细;看论文吧,又不知道从哪看起;真要准备机试,又发现手生的厉害。我自己当年备考“考研数据结构复试题目整理”的时…

2026/10/14 4:25:51 阅读更多 →
考虑主从配电网分布式优化的ADMM算法Matlab实现与串并行对比

考虑主从配电网分布式优化的ADMM算法Matlab实现与串并行对比

最近在推进一个关于主从配电网分布式优化控制的课题时,核心目标是用交替方向乘子法(ADMM)把主网和多个配电子网之间的协同优化拆开做,整个算法在Matlab里落地,并对比了串行和并行两种执行机制下的收敛行为。从模型搭建…

2026/10/14 4:25:51 阅读更多 →
2026 AI 论文工具排行榜|按「国内毕业论文适配度」打分,适合盲审抽检环境

2026 AI 论文工具排行榜|按「国内毕业论文适配度」打分,适合盲审抽检环境

前言 很多海外 AI 工具在国外学术写作里表现亮眼,但拿到国内本科、硕士毕业论文场景,就会水土不服。国内高校论文有一套独立规则:GB/T7714 参考文献格式、固定 Word 排版规范、AI 痕迹检测、参考文献溯源抽查、逻辑盲审。 本次排行榜不再看通…

2026/10/14 4:25:51 阅读更多 →
一次GC频繁问题排查:从JFR到代码优化的完整实践

一次GC频繁问题排查:从JFR到代码优化的完整实践

大概一个多月前,我接到一条让我头疼不已的告警:某个订单查询接口的 P99 延迟从平时的 80ms 一路飙到 800ms 以上,CPU 使用率每隔几分钟就像过山车一样冲到接近 100%。一开始我照例查了流量、数据库慢查询、第三方依赖,全都没有异常…

2026/10/14 4:25:51 阅读更多 →
pdf.js在线查看PDF工具:渲染原理、避坑指南与性能优化实践

pdf.js在线查看PDF工具:渲染原理、避坑指南与性能优化实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 4:25:51 阅读更多 →
Dropout 正则化原理与实战:从过拟合到泛化能力提升

Dropout 正则化原理与实战:从过拟合到泛化能力提升

1. 为什么一个"随手关掉神经元"的操作能成为深度学习的基石第一次在代码里写下nn.Dropout(p0.5)的时候,我其实没太当回事——不就是训练时随机扔掉一半神经元嘛,听起来像个正则化的小把戏。直到有一次复现一篇图像分类论文,模型在训…

2026/10/14 4:24:51 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →