LeetCode34:在排序数组中查找元素第一个和最后一个位置

原题地址:. - 力扣(LeetCode)

题目描述

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

解题思路

  • 二分查找:我们使用二分查找来查找目标值的左边界和右边界。在给定的有序数组中,使用二分查找可以减少搜索范围,从而达到 O(log n) 的时间复杂度。

    • 左边界查找:我们在二分查找时,使用一个标志 lower 来指示我们是查找目标值的左边界还是右边界。
      • 如果 lower 为 true,则我们会在目标值的位置停止时继续往左移动,从而找到目标值的最左边位置。
      • 如果 lower 为 false,则我们会在目标值的位置停止时继续往右移动,直到目标值的右边界。
  • 返回结果:通过两次二分查找分别获取左边界和右边界的索引。如果左边界小于等于右边界,并且这两个位置上的值都等于目标值,则返回这两个索引;否则,返回 [-1, -1]

源码实现

class Solution {public int[] searchRange(int[] nums, int target) {// 1. 使用二分查找找到目标值的左边界(lower = true)int leftIdx = binarySearch(nums, target, true);// 2. 使用二分查找找到目标值的右边界(lower = false),然后减去 1 获取实际的右边界int rightIdx = binarySearch(nums, target, false) - 1;// 3. 检查左边界和右边界是否有效,且这两个位置上的值是否为目标值if (leftIdx <= rightIdx && rightIdx < nums.length && nums[leftIdx] == target && nums[rightIdx] == target) {// 4. 返回目标值的左边界和右边界return new int[]{leftIdx, rightIdx};} // 5. 如果目标值不存在,返回 [-1, -1]return new int[]{-1, -1};}// 这里的 lower 参数用于控制是查找左边界(true)还是右边界(false)public int binarySearch(int[] nums, int target, boolean lower) {// 1. 初始化二分查找的左右边界int left = 0, right = nums.length - 1;// 2. 默认返回的答案是 nums.length,这样当目标值不存在时,能保证返回 [-1, -1]int ans = nums.length;while (left <= right) {int mid = (left + right) / 2;// 3. 根据目标值与中间值的比较来更新搜索范围// 4. 如果目标值小于中间值,或者是查找左边界时(lower = true)中间值大于等于目标值,// 则将搜索范围缩小到左半部分if (nums[mid] > target || (lower && nums[mid] >= target)) {right = mid - 1;ans = mid;} else {// 5. 如果目标值大于中间值,则搜索范围缩小到右半部分left = mid + 1;}}// 6. 返回找到的索引位置(若没有找到,则返回 nums.length)return ans;}
}

复杂度分析

  • 时间复杂度

    • 每次调用 binarySearch 都是 O(log n),其中 n 是数组的长度。
    • 在 searchRange 方法中,调用了两次 binarySearch,一次查找左边界,另一次查找右边界。因此总时间复杂度为 O(log n)
  • 空间复杂度

    • 该算法只使用了常量级的额外空间(除了返回结果数组),因此空间复杂度是 O(1)

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

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

相关文章

2024年Python最新下载安装教程,附详细图文,持续更新

大家好&#xff0c;我是Java徐师兄&#xff0c;今天为大家带来的是Python3 的下载安装教程&#xff0c;适用于所有 Python3 版本&#xff0c;感兴趣的可以看一下 文章目录 简介一、Python的下载1 网盘下载地址 (下载速度比较快&#xff0c;推荐&#xff09;2 官网下载地址 二、…

原生鸿蒙应用市场:赋能开发者全生命周期服务体验

文章目录 背景自动化检测前移&#xff1a;早发现&#xff0c;早解决技术细节&#xff1a;静态代码分析与兼容性测试应用场景 按需加载&#xff1a;优化性能&#xff0c;提升用户体验技术细节&#xff1a;模块化与懒加载实现应用场景 应用加密&#xff1a;保护应用代码安全&…

苍穹外卖 数据可视化

将营业额、用户数据、订单数据、商品销量top10数据全部使用Apache Echarts可视化&#xff0c;展现在前端&#xff0c;后端只需要按照需要的格式&#xff0c;为前端提供数据即可。 ReportController package com.sky.controller.admin;import com.sky.result.Result; import co…

漏洞与攻击技术详解

漏洞与攻击技术是网络安全领域中的重要议题&#xff0c;它们之间存在着密切的关系。以下是对漏洞与攻击技术的详细分析&#xff1a; 一、漏洞的定义与分类漏洞是指在硬件、软件、协议的具体实现或系统安全策略上存在的缺陷&#xff0c;这些缺陷使得攻击者能够在未授权的情况下访…

kafka面试题解答(四)

5、消费者组和分区数之间的关系是怎样的&#xff1f; 消费者组数小于等于分区数&#xff0c;消费者组内每个消费者负责消费不同分区的数据&#xff0c;一个分区只能由一个组内消费者消费。 6、kafka如何知道哪个消费者消费哪个分区&#xff1f; 生产者把数据发送给各个分区&…

鸿蒙华为商城APP案例

模拟器运行效果如下&#xff1a; 鸿蒙版APP-华为商城-演示视频

C++【STL容器系列(二)】vector的模拟实现

文章目录 1. vector的结构2. vector的默认成员函数2.1构造函数2.1.1 默认构造2.1.2 迭代器构造2.1.3 用n个val初始化构造 2.2 拷贝构造2.3 析构函数2.4 operator 3. vector iterator函数3.1 begin 和 cbegin函数3.2 end() 和 cend()函数 4. vector的小函数4.1 size函数4.2 capa…

git修改当前分支名称并推送到远程仓库

git修改当前分支名称并推送到远程仓库 在Git中修改当前分支的名称可以通过两种主要方式进行&#xff1a;直接在本地修改分支名称&#xff0c;或者如果你已经推送了分支到远程仓库&#xff0c;你也需要更新远程分支的名称。以下是详细步骤&#xff1a; 修改本地分支名称 查看当…

第 8 章 - Go语言 数组与切片

在Go语言中&#xff0c;数组和切片是两种非常基础且重要的数据结构。它们都用来存储一系列相同类型的元素&#xff0c;但是它们之间存在一些关键的区别。下面我们将详细探讨数组和切片的定义、使用以及切片的动态特性。 数组的定义和使用 定义 在Go语言中&#xff0c;数组是…

Linux开发讲课49--- Linux 启动过程分析

理解运转良好的系统对于处理不可避免的故障是最好的准备。 启动过程非常简单。内核在单核上以单线程和同步状态启动&#xff0c;似乎可以理解。但内核本身是如何启动的呢&#xff1f;initrd&#xff08;initial ramdisk&#xff09; 和引导程序(bootloader)具有哪些功能&#…

vscode中执行git合并操作需要输入合并commit信息,打开的nano小型文本编辑器说明-

1.前提: VScode中的git组件执行任何合并动作的时候需要提交远程合并的commit信息,然后编辑器自动打开的是nano文本编辑器 2.nano编辑器说明: 1.保存文件:按 Ctrl + O,然后按 Enter 来保存文件。 2.退出编辑器:按 Ctrl + X,这会退出 nano。 3.剪切文本:移动光标到要剪…

Java 并发相关集合

文章目录 一、CopyOnWriteArrayList 源码1.1. 概述1.2. 思想1.3. 源码① 数据结构② 初始化③ 添加元素④ 获取元素⑤ 删除元素 二、ArrayBlockingQueue 源码2.1. 概述2.2. 思想2.3. 源码① 数据结构② 初始化③ 阻塞式获取和新增元素④ 非阻塞式获取和新增元素⑤ 指定超时时间…

Mysql个人八股总结

1.一条 SQL 查询语句是如何执行的 第一步&#xff1a;连接器 连接数据库&#xff1a;当用户发起SQL查询时&#xff0c;连接器负责与数据库建立连接&#xff0c;验证用户身份并准备执行查询。 第二步&#xff1a;查询缓存 检查查询缓存&#xff1a;在执行查询之前&#xff0…

MySQL数据导入与清洗

在现代数据分析的工作流程中,数据导入与清洗是最基础且重要的环节。无论是通过CSV、Excel,还是SQL文件进行数据导入,数据清洗的操作对于数据的质量至关重要。高质量的数据源是后续分析的根本,数据清洗可以帮助分析者获得干净、整洁且可靠的数据集,减少数据噪音,提升分析的…

Linux如何更优质调节系统性能

一、硬件优化 增加物理内存&#xff1a;最直接的提升系统性能的方法。内存不足时&#xff0c;系统会频繁进行交换&#xff08;swapping&#xff09;活动&#xff0c;这会显著降低系统的响应速度&#xff0c;因为磁盘IO速度远低于内存访问速度。通过增加内存&#xff0c;可以减…

GET和POST的区别

GET 和 POST 是 HTTP 协议中最常用的两种请求方法&#xff0c;它们在用途、安全性、数据处理等方面存在显著差异。下面是 GET 和 POST 的详细对比&#xff1a; GET 请求 1. 用途&#xff1a; 主要用于从服务器获取数据。通常用于检索信息&#xff0c;如搜索、查询数据库等。…

AutoDL使用简记

AutoDL使用简记 一、前言二、AutoDL显卡配置、价格简介2.1显卡配置及价格2.2计费方式的种类2.3开通会员及优惠 三、AutoDL使用教程3.1选择深度学习架构3.2文件传输3.3运行程序 一、前言 在进行深度学习模型训练时&#xff0c;通常会面临本地显卡显存或者运行速度的不足&#x…

基于STM32智能电流表

采用STM32F103C8T6微控制器为核心&#xff0c;设计了一款精密的电流表。该电流表通过精确采集采样电阻上的分压信号&#xff0c;并进行信号放大处理&#xff0c;随后利用ADC&#xff08;模数转换器&#xff09;高效地捕获放大后的电压信号&#xff0c;通过一系列算法运算&#…

【harbor】离线安装2.9.0-arm64架构服务制作和升级部署

harbor官网地址&#xff1a;Harbor 参考文档可以看这里&#xff1a;部署 harbor 2.10.1 arm64 - 简书。 前提环境准备&#xff1a; 安装docker 和 docker-compose 先拉arm64架构的harbor相关镜像 docker pull --platformlinux/arm64 ghcr.io/octohelm/harbor/harbor-regist…

Java API类与接口:类的转换方法与正则表达式

文章目录 Java包装类的概述对应包装类包装类的转换方法&#xff08;parse)Integer.parseInt(String s)Long.parseLong(String s)Byte.parseByte(String s)Short.parseShort(String s)Float.parseFloat(String s)Double.parseDouble(String s) 正则表达式常用方法 字符规则. 匹配…