在排序数组中查找元素的第一个和最后一个位置(Java详解)

一、题目描述

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

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

你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。

示例:

输入:nums = [5,7,7,8,8,10],target = 8

输出:[3, 4]

输入:nums = [5,7,7,8,8,10],target = 6

输出:[-1, -1]

输入:nums = [ ],target = 0

输出:[-1, -1]

二、题解

思路分析:

题目要求我们找到出现target的第一个位置和最后一个位置,首先,我们想到可以通过暴力枚举的方法来解决该问题,即遍历数组,并记录target第一次出现和最后一次出现的位置。然而,题目要求我们实现时间复杂度为O(log n)的算法,且题目中给出的数组为非递减的数组,因此,我们可以考虑使用二分查找的方法来解决该问题

由于题目中数据量较小,使用遍历的方法也可以通过该题

遍历代码:

class Solution {public int[] searchRange(int[] nums, int target) {int first = -1;int last = -1;boolean flg = false;//判断是否是第一个位置for (int i = 0; i < nums.length; i++) {//第一个位置if(nums[i] == target && !flg){first = i;flg = true;}//最后一个位置//注意处理特殊情况,即最后一个元素在最后一个位置时,nums[i+1]越界if(nums[i] == target && (i == nums.length - 1 || nums[i+1] != target)){last = i;}}int[] ret = {first,last};return ret;}
}

如何使用二分查找来解决该问题?

题目要求我们找到target在数组中第一次出现和最后一次出现的位置,利用数组非递减的性质

首先我们查找元素的第一个位置:

 我们可通过target将数组分为两部分

其中,左边部分为小于target的元素,右边部分为大于等于target的元素,由于右边区域大于等于target,因此区域最左边的值即为target第一次出现的位置,即右边区域的左端点为所求结果

我们定义left指向0位置,right指向最后一个元素,mid指向中间位置

若mid指向的元素落在右边区间,此时nums[mid]大于等于target,需要更新right的值,由于要找的结果(target第一次出现的位置)在此区间内,即mid所指向的位置可能就是最终结果,因此不能将right更新为mid - 1,而应更新为mid

若mid所指向的元素落在左边区间,此时nums[mid]小于target,需要更新 left 的值,由于要找的结果不在此区间内,因此可将left的结果更新为 mid + 1

 当left和right之间元素为偶数个时,此时中间元素有两个,应该选择哪一个作为中间元素?

由于我们查找的是右边区间内最左边的元素,因此,应该选择左边的元素作为中间元素

若选择右边元素作为中间元素,能够成功查找到结果吗? 

当选择右边元素作为中间元素时,此时会出现死循环的情况,例如: 

 上图中,当选择右边元素作为中间元素时,mid指向的元素落在右边区间,此时将right更新为mid,再求mid,此时mid仍为指向刚才位置,即落在右边区间,此时再次更新right为mid,再次求mid... 从而死循环

循环条件如何设置?

 由于我们将right更新为mid,因此循环的条件应为left < right,若循环条件设置为left <= right,当left = right时,此时找到结果,而结果落在右边区间,此时会更新right的值,而right 更新为mid,即当前位置,从而死循环

分析完以上问题后,我们可以尝试编写查找右边区域最左边元素的代码:

//查找target第一次出现的位置(右边区间的左端点)
int left = 0,right = nums.length - 1,mid = left + (right - left)/2;
//循环条件应设置为left < right 
//不能设置为left <= right,否则会死循环
while(left < right){//当mid所指向的元素落在左边区间时,更新leftif(nums[mid] < target){left = mid + 1;}else{//当mid所指向的元素落在右边区间时,此时更新right//由于右边区间的元素大于等于target,即结果在该区间内,// 因此不能将right更新为mid - 1,而应更新为midright = mid;}//更新mid,当有两个中间元素时,mid应指向其中左边的元素mid = left + (right - left) / 2;
}

 此时我们查找target最后一次出现的位置

与查找第一次出现位置的思路相同,我们首先将数组分为两个部分:

其中,左边区间内元素小于等于target,右边区间元素大于target

此时,要找的结果即为左边区间的右端点

 同样的,定义left指向0位置,right指向最后一个元素,mid指向中间位置

若mid所指向的元素落在左边区间,此时需要更新left的值,由于要找的结果落在此区间内,即mid所指向的位置可能就是最终结果,因此不能将left更新为mid + 1,而应更新为mid

若mid所指向的元素落在右边区间,此时需要更新right的值,由于要找的结果不在右边区间,因此可将right的值更新为mid - 1

当left和right之间元素为偶数个时,此时中间元素有两个,应该选择哪一个作为中间元素?

 由于我们查找的是左边区间内最右边的元素,因此,应该选择右边的元素作为中间元素

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

同样的,当选择左边元素作为中间元素时,也会造成死循环

此时left的值一直更新为当前位置,造成死循环

循环条件的设置:

循环条件也同样应该设置为left < right,否则会死循环

此时我们尝试编写查找左边区间右端点代码:

//查找区间右端点
left = mid;
right = nums.length - 1;
mid = left + (right - left + 1)/2;
while(left < right){//当mid所指向的值落在右边区域时,更新右端点if(nums[mid] > target){right = mid - 1;}else{//当mid所指向的值落在左边区域时,更新左端点//由于左边区间的元素小于等于target,即结果在该区间内,//因此不能将left更新为mid + 1,而应更新为midleft = mid;}//更新mid的值,若有两个中间元素时,mid应指向其中右边的元素mid = left + (right - left + 1) / 2;
}

完整代码:

class Solution {public int[] searchRange(int[] nums, int target) {int[] ret = {-1,-1};//若数组为空,直接返回retif(nums.length == 0){return ret;}//查找target第一次出现的位置(右边区间的左端点)int left = 0,right = nums.length - 1,mid = left + (right - left) / 2;//循环条件应设置为left < right//不能设置为left <= right,否则会死循环while(left < right){//当mid所指向的元素落在左边区间时,更新leftif(nums[mid] < target){left = mid + 1;}else{//当mid所指向的元素落在右边区间时,此时更新right//由于右边区间的元素大于等于target,即结果在该区间内,// 因此不能将right更新为mid - 1,而应更新为midright = mid;}//更新mid,当有两个中间元素时,mid应指向其中左边的元素mid = left + (right - left) / 2;}//此时left = right = mid,使用哪一个变量进行判断和更新都可以//若数组中无值为target的元素,直接返回retif(nums[left] == target){ret[0] = left;}else{return ret;}//查找区间右端点left = mid;right = nums.length - 1;mid = left + (right - left + 1)/2;while(left < right) {//当mid所指向的值落在右边区域时,更新右端点if (nums[mid] > target) {right = mid - 1;} else {//当mid所指向的值落在左边区域时,更新左端点//由于左边区间的元素小于等于target,即结果在该区间内,//因此不能将left更新为mid + 1,而应更新为midleft = mid;}//更新mid的值,若有两个中间元素时,mid应指向其中右边的元素mid = left + (right - left + 1) / 2;}ret[1] = left;return ret;}
}

题目来自: 

34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)

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

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

相关文章

OpenTiny Vue 组件库3.12.0 发布:文档大优化!增加水印和二维码两个新组件

非常高兴跟大家宣布&#xff0c;2023年11月30日&#xff0c;OpenTiny Vue 发布了 v3.12.0 &#x1f389;。 OpenTiny 每次大版本发布&#xff0c;都会给大家带来一些实用的新特性&#xff0c;10.24 我们发布了 v3.11.0 版本&#xff0c;增加了富文本、ColorPicker 等4个新组件…

linux下查看进程资源ulimit

ulimit介绍与使用 ulimit命令用于查看和修改进程的资源限制。下面是ulimit命令的使用方法&#xff1a; 查看当前资源限制&#xff1a; ulimit -a 这将显示当前进程的所有资源限制&#xff0c;包括软限制和硬限制。查看或设置单个资源限制&#xff1a; ulimit -<option> …

喜报丨迪捷软件入选2023年浙江省信息技术应用创新典型案例

12月6日&#xff0c;浙江省经信厅公示了2023年浙江省信息技术应用创新典型案例入围名单。本次案例征集活动&#xff0c;由浙江省经信厅、省密码管理局、工业和信息化部网络安全产业发展中心联合组织开展&#xff0c;共遴选出24个优秀典型解决方案&#xff0c;迪捷软件“基于全数…

安装鸿蒙开发者工具DevEco Studio

1.进入官网下载工具 https://developer.harmonyos.com/cn/develop/deveco-studio/ 选择您电脑对应的系统下载即可 2.安装 很简单直接点击“next”,此处不做赘述 3.配置环境 安装完成后&#xff0c;打开DevEco Studio 会提示配置环境。安装node.js和ohpm 如果不小心关了&a…

DevEco Studio无法识别本地模拟器设备的解决方法

遇到了一个问题&#xff0c;之前测试无误的本地模拟器&#xff0c;运行后设备栏中无法识别了。 此时保持模拟器处于开启状态&#xff0c;关闭DevEco Studio窗口重新启动后&#xff0c;发现重新识别设备了。

【CASS精品教程】cass11提示“请不要在虚拟机中运行此程序”的解决办法

文章目录 一、问题提示二、解决办法一、问题提示 按照正常安装教程安装好南方测绘cass 11之后,打开的时候可能会有以下提示:请不要在虚拟机中运行此程序,如下图所示: 遇到问题,咱们就想办法解决问题,下面将自己尝试的方法及最终解决情况跟大家说一下,供参考。 二、解决…

Linux---压缩和解压缩命令

1. 压缩格式的介绍 Linux默认支持的压缩格式: .gz.bz2.zip 说明: .gz和.bz2的压缩包需要使用tar命令来压缩和解压缩.zip的压缩包需要使用zip命令来压缩&#xff0c;使用unzip命令来解压缩 压缩目的: 节省磁盘空间 2. tar命令及选项的使用 命令说明tar压缩和解压缩命令 …

Linux centos7安装redis 6.2.14 gz并且使用systemctl为开机自启动 / 彻底删除 redis

1.下载 && 减压 wget http://download.redis.io/releases/redis-6.2.14.tar.gz tar -zvxf redis-6.2.14.tar.gz 2.编译&#xff08;分开运行&#xff09; cd redis-6.2.14 make cd src make install 安装目录展示 3.redis.conf 配置更改 daemonize yes supervised s…

智能优化算法应用:基于JAYA算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于JAYA算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于JAYA算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.JAYA算法4.实验参数设定5.算法结果6.参考文献7.MA…

IDEA配置一个新项目

git clone xxxxx 下载项目主分支 git checkout xxx 切换到需要开发的分支上 配置maven仓库 在File下的Settings中设置maven仓库 配置maven仓库的文件夹 配置好maven后&#xff0c;项目中会出现一个红色的pom.xml文件&#xff0c;右击文件&#xff0c;点击…&#xff0c;pom…

配置Nginx解决跨域问题

Nginx 中将前端请求中的所有以 “/apiUrl” 开头的路径代理到 http://192.12.200.101:9813 例如&#xff1a; /apiUrl/login > http://192.12.200.101:9813/login 配置nginx环境 进入Nginx 的配置文件编辑界面: sudo nano /etc/nginx/conf.d/default.conf开始编辑 defaul…

基于Springboot的旅游网站设计与实现(论文+调试+源码)

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下&#xff0c;你想解决的问…

数据手册Datasheet解读-MOS管笔记

数据手册Datasheet解读-MOS管笔记 NMOS应用场景一般特征第一个参数Vdss第二、三个参数Rds&#xff08;on&#xff09;、IdMOS管的散热绝对最大额定值第一个参数-Vd第二个参数-Vdgr第三个参数-Vg(栅源电压)第四个参数-Id第五个参数-Idm第六个参数-Ptot第七个参数-Viso第七和八的…

模块四(一):搭建自己的SSR

前言&#xff1a;同构渲染是将服务器渲染和客户端渲染相结合的一种渲染方式&#xff0c;在服务端生成初始页面&#xff0c;提升首屏加载速度&#xff0c;并且有利于SEO&#xff1b;在客户端接管HTML&#xff0c;并且将静态HTML激活为数据绑定的动态HTML&#xff0c;为用户提供更…

Unity 常用资料

1. Layer layer本质是按序左移的一个Int32数字&#xff0c;int32有4个字节》32位&#xff0c;故可以支持32个层级。系统自身使用了5个层&#xff0c;意味着用户还有27个层可以使用。 0000 0000 0000 0000 0000 0000 0000 0000 可以且只能任意位为1。unity具体的0-31的层级分布…

Python实战 | 如何抓取腾讯视频

嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 爬虫: 作用: 批量采集数据 / 模拟用户行为 原理: 模拟成 客户端 向 服务器 发送网络请求 环境介绍: python 3.8 解释器 pycharm 编辑器 第三方模块: reques…

委托线程安全

委托线程安全是指将对共享数据的访问委托给线程安全的类&#xff0c;从而避免在调用方代码中进行额外的同步控制。这可以通过将线程安全性限定在一个类内部来实现。以下是一个简单的例子&#xff1a; 假设有一个计数器类 Counter&#xff0c;它不是线程安全的。 public class…

使用git命令行来拉取项目

哈哈哈&#xff0c;在校的我可能更偏向用git&#xff0c;可是在实习过程中大家都用的是图形化工具&#xff0c;因为还不是太熟练&#xff0c;所以我目前还是在用的git命令行。 下边说一下大致流程吧&#xff01; //拉项目 git clone 仓库地址 //根据项目来安装依赖 npm或者y…

公众号提高上限怎么操作?

一般可以申请多少个公众号&#xff1f;众所周知&#xff0c;在2013年前后&#xff0c;公众号申请是不限制数量的&#xff0c;后来企业开始限制申请50个&#xff0c;直到2018年的11月tx又发布&#xff0c;其中个人主体可申请公众号由2个调整为1个&#xff0c;企业主体由50个调整…

CAPL——发送自定义报文

文章目录 一、前言二、CANoe操作二、CAPL程序三、Trace结果一、前言 CAPL是CANoe自带的一个编程语言,基本语法基于C语言,通过CAPL可以发挥CANoe更高效、更强大的功能。CAPL最大的特点就是可以编程灵活的完成报文的发送,报文包括通信报文及诊断报文 本文讲述模拟一个报文周…