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

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

目录

  • 34. 在排序数组中查找元素的第一个和最后一个位置
    • 题目描述
    • 思路
      • 核心思想
      • 关键点
    • 代码
      • 解法一:闭区间[l, r]
      • 解法二:左闭右开[l, r)
      • 解法三:开区间(l, r)
    • 答疑
      • Q: 闭区间写法中,nums[mid] >= target时为什么是r = mid - 1?mid不可能是答案吗?不会错过正确答案吗?
    • 复杂度分析

题号: “34”
难度: 中等
标签: 二分查找,数组
链接: https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/description/

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

题目描述

给定一个升序排列的整数数组nums和一个目标值target,找出target在数组中的第一个最后一个位置;若数组中不存在target,返回[-1, -1]

  • 输入:nums(升序数组,可能含重复元素)、target
  • 输出:[start, end](下标),不存在时返回[-1, -1]
  • 进阶: 要求时间复杂度为 O(log n)

思路

核心思想

两次二分,分别定位左边界与右边界:

  • 左边界=lowerBound(nums, target):第一个>= target的下标
  • 右边界=lowerBound(nums, target + 1) - 1:即「第一个> target的位置」再往前一位,得到最后一个<= target的下标

start == nnums[start] != target,说明目标不存在,返回[-1, -1];否则返回[start, end](起点存在时,终点必然存在)。

关键点

完整的需求转化表见 [[二分查找模板]],本题只需用到其中两行:

需求写法不存在时
第一个>= x的下标lowerBound(nums, x)n
最后一个<= x的下标lowerBound(nums, x + 1) - 1-1

两次二分相互独立,各 O(log n),总复杂度 O(log n)。

代码

三种区间写法的lowerBound行为完全一致,searchRange主逻辑共用。默认推荐左闭右开(与 C++ STLlower_bound语义一致)。

解法一:闭区间[l, r]

classSolution{public:intlowerBound(vector<int>&nums,inttarget){intl=0,r=nums.size()-1;while(l<=r){intmid=l+(r-l)/2;// 防止溢出if(nums[mid]>=target)r=mid-1;// 答案至多为 mid,收缩右边界elsel=mid+1;}returnl;// 或 r + 1}vector<int>searchRange(vector<int>&nums,inttarget){intstart=lowerBound(nums,target);if(start==nums.size()||nums[start]!=target)return{-1,-1};// 起点不存在,终点必然不存在intend=lowerBound(nums,target+1)-1;return{start,end};}};

解法二:左闭右开[l, r)

classSolution{public:intlowerBound(vector<int>&nums,inttarget){intl=0,r=nums.size();// 区间 [0, n),r 取 n 可表示越界while(l<r){intmid=l+(r-l)/2;if(nums[mid]>=target)r=mid;// 答案在 [l, mid] 内,保留 midelsel=mid+1;}returnl;// 或 r}vector<int>searchRange(vector<int>&nums,inttarget){intstart=lowerBound(nums,target);if(start==nums.size()||nums[start]!=target)return{-1,-1};intend=lowerBound(nums,target+1)-1;return{start,end};}};

解法三:开区间(l, r)

classSolution{public:intlowerBound(vector<int>&nums,inttarget){intl=-1,r=nums.size();// 区间 (-1, n),哨兵可表示边界while(l+1<r){intmid=l+(r-l)/2;// 循环保证 r - l >= 2,mid 必在区间内if(nums[mid]>=target)r=mid;elsel=mid;}returnr;// 或 l + 1}vector<int>searchRange(vector<int>&nums,inttarget){intstart=lowerBound(nums,target);if(start==nums.size()||nums[start]!=target)return{-1,-1};intend=lowerBound(nums,target+1)-1;return{start,end};}};

答疑

Q: 闭区间写法中,nums[mid] >= target时为什么是r = mid - 1?mid不可能是答案吗?不会错过正确答案吗?

关键在于区分二分范围答案所在范围

lowerBound维护的循环不变量是:答案(第一个>= target的位置)始终落在[l, r + 1]。当nums[mid] >= target时,mid已经满足条件,而答案必须是「第一个」满足条件的位置,所以答案至多为mid——mid右侧全部排除,二分范围收缩为[l, mid - 1],而可能答案mid由边界r + 1携带,不会被丢弃。

同理,若target大于区间内所有元素,循环结束时l == r + 1,答案正是l二分收缩的是候选区间,答案由边界l/r携带,永不丢失。其余两种写法同理:左闭右开由r携带,开区间由r(或l + 1)携带。

复杂度分析

时间复杂度空间复杂度说明
O(log n)O(1)两次二分各 O(log n);原地操作,无额外空间
http://www.cnnetsun.cn/news/3777198.html

相关文章:

  • 158、TinyML模型训练最佳实践:持续学习
  • Java浮点数精度处理:BigDecimal、DecimalFormat等5种保留小数方法详解
  • UniApp混合开发:自定义Application与Activity实现双击返回键退出
  • AI写论文靠谱吗?2026年学长实测的正确打开方式
  • 嵌入式调试核心指南:JTAG/SWD协议与J-Link/ST-Link调试器选型实战
  • MLP国配第一季翻译问题分析:文化差异与本地化策略
  • 《炼金与魔法》:国产沙盒游戏的炼金系统与双人联机体验
  • 《吞食天地2:蜀汉英雄传》1.5版深度攻略:系统解析与全流程图文指南
  • 电容式触摸屏原理与架构解析:从自互电容到In-Cell技术
  • 动态规划状态机精解:买卖股票的最佳时机 III 问题
  • Unity异步场景加载优化:基于UniTask的状态机设计与性能实践
  • 朴素贝叶斯分类器原理与文本分类实战
  • 跨境电商物流自动化实践:基于DHL/FedEx/UPS API的运费优化与渠道选型算法
  • Hive大数据分析入门:从SQL到分布式查询引擎的实战指南
  • HideMockLocation终极指南:3步轻松隐藏模拟位置不被检测
  • 老年人跌倒检测物联网数据集:用于实时跌倒监测的多模态可穿戴与环境传感器数据
  • 从北大软微拟录取名单看考研竞争:信息战、策略与心态博弈
  • 串口通信核心:波特率9600原理、配置与调试全解析
  • 状态机中after计时计数模式的深度解析与实践指南
  • git使用时记住用户名和密码
  • 对账流程的 OGNL 变量完整数据流
  • 格雷码与二进制转换:原理、C语言实现与工程应用
  • Epoch、Batch 与 DataLoader
  • 点击化学:从CuAAC到SPAAC,掌握模块化分子连接的底层逻辑与实战指南
  • C++ vector多维数组初始化:一行代码实现高效内存管理
  • 同样的 Agent,换了一套提示词,效果翻了 5 倍:Skill 工程实战指南
  • GPT文本生成原理与采样策略优化实践
  • 工业级PID控制器C语言实现:从离散化到抗饱和与参数整定
  • MATLAB图像处理实战:空域与频域方法消除条纹干扰
  • Unity与Cocos2d-x双引擎实现Flappy Bird:源码对比与实战解析