LeetCode 热题 100_在排序数组中查找元素的第一个和最后一个位置(65_34_中等_C++)(二分查找)(一次二分查找+挨个搜索;两次二分查找)

news2025/2/23 17:22:48

LeetCode 热题 100_在排序数组中查找元素的第一个和最后一个位置(65_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

题解:

解题思路:

思路一(一次二分查找+挨个搜索):

1、通过二分查找找到第一个等于target的元素,若没查找到等于target的元素则返回[-1,-1]。若查找到target的元素,则从此位置开始分别向左向右挨个判断元素值是否为target,直至元素值不等于target为止,此时更新最左侧和最右侧的下标。

2、复杂度分析:
① 时间复杂度:O(n),n代表数组中元素的个数,最坏的情况:数组中的元素都为target。
② 空间复杂度:O(1)。

思路二(两次二分查找):

1、可以通过二分查找先查找到等于target值元素的最左侧下标,再采用二分查找找到等于target值元素的最右侧的下标。
① 当元素值等于target值的时候,再在左侧区间查找是否有等于target值的元素,有则更新等于target的左侧下标,直至左侧子区间不存在等于target值的元素。
② 当元素值等于target值的时候,再在右侧区间查找是否有等于target值的元素,有则更新等于target的右侧下标,直至右侧子区间不存在等于target值的元素。

2、复杂度分析
① 时间复杂度:O(logn),n代表数组中元素的个数。
② 空间复杂度:O(1)。

代码实现

代码实现(思路一(二分查找+挨个搜索)):
class Solution1 {
    public:
        vector<int> searchRange(vector<int>& nums, int target) {
            // 初始化返回的结果为 {-1, -1},表示未找到目标值
            vector<int> ans = {-1, -1};

            // 如果数组为空,直接返回 {-1, -1}
            if (nums.empty())
            {
                return ans;
            }
            
            // 初始化二分查找的左右边界
            int left = 0, right = nums.size() - 1;
            int mid;
            
            // 使用二分查找寻找目标值的某个位置
            while (left <= right)
            {
                // 计算中间位置
                mid = left + (right - left) / 2;
           
                // 如果中间值大于目标值,缩小右边界
                if (nums[mid] > target)
                {
                    right = mid - 1;
                }
                // 如果中间值小于目标值,缩小左边界
                else if (nums[mid] < target) {
                    left = mid + 1;
                }
                // 如果找到了目标值,记录当前中间值位置,并跳出循环
                else {
                    ans[0] = mid; // 目标值的起始位置
                    ans[1] = mid; // 目标值的结束位置
                    break; // 找到目标值,跳出循环
                }
            }
            
            // 如果找到了目标值(即ans[0]不为-1)
            if (ans[0] != -1)
            {
                // 在找到的位置周围扩展,查找目标值的左边界和右边界
                left = ans[0];
                right = ans[1];
                
                // 从左边界开始,向左扩展,直到不等于目标值为止
                while (left >= 0 && nums[left] == target){
                    left--;
                }
                // 更新目标值的左边界
                ans[0] = left + 1;

                // 从右边界开始,向右扩展,直到不等于目标值为止
                while (right < nums.size() && nums[right] == target){
                    right++;
                }
                // 更新目标值的右边界
                ans[1] = right - 1;
            }
            
            // 返回目标值的范围
            return ans;
        }
};
代码实现(思路二(两次二分查找)):
class Solution2 {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        vector<int> ans = {-1, -1};
        
        if (nums.empty()) {
            return ans;
        }
        
        // 寻找左边界
        int left = 0, right = nums.size() - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target) {
                left = mid + 1;
            } else if (nums[mid] > target) {
                right = mid - 1;
            } else {
                // 找到目标,继续向左移动 right 确保是最左边的位置
                right = mid - 1;
            }
        }
        //这里不能使用left<=right来判断是否找到target,因我们再继续查找其他的target时会令最终的left>right
        // 如果 left 超出了数组范围,或者 nums[left] 不是 target,说明 target 不存在
        if (left >= nums.size() || nums[left] != target) {
            return ans;
        }
        ans[0] = left;
        
        // 寻找右边界
        right = nums.size() - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] > target) {
                right = mid - 1;
            } else if (nums[mid] < target) {
                left = mid + 1;
            } else {
                // 找到目标,继续向右移动 left 确保是最右边的位置
                left = mid + 1;
            }
        }
        ans[1] = right;
        
        return ans;
    }
};
以思路二为例进行调试
#include<iostream>
#include <vector>
using namespace std;

class Solution2 {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        vector<int> ans = {-1, -1};
        
        if (nums.empty()) {
            return ans;
        }
        
        // 寻找左边界
        int left = 0, right = nums.size() - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target) {
                left = mid + 1;
            } else if (nums[mid] > target) {
                right = mid - 1;
            } else {
                // 找到目标,继续向左移动 right 确保是最左边的位置
                right = mid - 1;
            }
        }
        //这里不能使用left<=right来判断是否找到target,因我们再继续查找其他的target时会令最终的left>right
        // 如果 left 超出了数组范围,或者 nums[left] 不是 target,说明 target 不存在
        if (left >= nums.size() || nums[left] != target) {
            return ans;
        }
        ans[0] = left;
        
        // 寻找右边界
        right = nums.size() - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] > target) {
                right = mid - 1;
            } else if (nums[mid] < target) {
                left = mid + 1;
            } else {
                // 找到目标,继续向右移动 left 确保是最右边的位置
                left = mid + 1;
            }
        }
        ans[1] = right;
        
        return ans;
    }
};

int main(int argc, char const *argv[])
{
    vector<int> nums={5,7,7,8,8,10};
    int target= 8;
    Solution2 s2;
    vector<int>ans= s2.searchRange(nums,target);
    cout<<ans[0]<<"  "<<ans[1];
    return 0;
}

LeetCode 热题 100_在排序数组中查找元素的第一个和最后一个位置(65_34)原题链接
欢迎大家和我沟通交流(✿◠‿◠)

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

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

相关文章

洛谷 P1102 A-B 数对(详解)c++

题目链接&#xff1a;P1102 A-B 数对 - 洛谷 1.题目分析 2.算法原理 解法一&#xff1a;暴力 - 两层for循环 因为这道题需要你在数组中找出来两个数&#xff0c;让这两个数的差等于定值C就可以了&#xff0c;一层for循环枚举A第二层for循环枚举B&#xff0c;求一下看是否等于…

计算机视觉:主流数据集整理

第一章&#xff1a;计算机视觉中图像的基础认知 第二章&#xff1a;计算机视觉&#xff1a;卷积神经网络(CNN)基本概念(一) 第三章&#xff1a;计算机视觉&#xff1a;卷积神经网络(CNN)基本概念(二) 第四章&#xff1a;搭建一个经典的LeNet5神经网络(附代码) 第五章&#xff1…

2025软件测试面试常问的题(详细解析)

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 测试技术面试题 1、什么是兼容性测试&#xff1f;兼容性测试侧重哪些方面&#xff1f; 参考答案&#xff1a; 兼容测试主要是检查软件在不同的硬件平台、软件平…

项目POC的作用是什么

在项目管理和开发中&#xff0c;POC&#xff08;Proof of Concept&#xff0c;概念验证&#xff09;作为一个关键的步骤&#xff0c;扮演着非常重要的角色。POC的作用主要是验证某个概念、技术或方案的可行性&#xff0c;通过小规模实验或原型验证项目的关键假设&#xff0c;帮…

集合 数据结构 泛型

文章目录 1.Collection集合1.1数组和集合的区别【理解】1.2集合类体系结构【理解】1.3Collection 集合概述和使用【应用】内部类匿名内部类Lambda表达式 1.4Collection集合的遍历【应用】1.5增强for循环【应用】 2.List集合2.1List集合的概述和特点【记忆】2.2List集合的特有方…

vue-treeselect显示unknown的问题及解决

问题 解决办法 去node-modules包里面找到这个组件的源码&#xff0c;在它dist文件里面找到这个文件&#xff0c;然后搜索unknown&#xff0c;把它删掉就可以解决了。

代码随想录-训练营-day35

309. 买卖股票的最佳时机含冷冻期 - 力扣&#xff08;LeetCode&#xff09; 这个题比起我们的买卖股票二来说多了一个冷冻期的说法&#xff0c;也就是我们卖出股票的第二天无法买入股票。 这样对我们而言&#xff0c;dp数组的含义&#xff0c;或者说dp数组中的状态显然就不能是…

【Blender】二、建模篇--06,曲线建模/父子级和蒙皮修改器

00:00:03,620 --> 00:00:09,500 前几节可能我们已经做了很多种类型的模型了 但是有一种类型 我们一直避开就是这种管道 1 00:00:10,050 --> 00:00:19,370 藤条头发啊 衣服架子啊这种弯弯绕绕的 需要一定柔软度的模型 那么这节课呢我们都来集中看一下曲线的模型 我们应该…

【服务器与本地互传文件】远端服务器的Linux系统 和 本地Windows系统 互传文件

rz 命令&#xff1a;本地上传到远端 rz 命令&#xff1a;用于从本地主机上传文件到远程服务器 rz 是一个用于在 Linux 系统中通过 串口 或 SSH 上传文件的命令&#xff0c;它实际上是 lrzsz 工具包中的一个命令。rz 命令可以调用一个图形化的上传窗口&#xff0c;方便用户从本…

被裁20240927 --- WSL-Ubuntu20.04安装cuda、cuDNN、tensorRT

cuda、cuDNN、tensorRT的使用场景 1. CUDA&#xff08;Compute Unified Device Architecture&#xff09; 作用&#xff1a; GPU 通用计算&#xff1a;CUDA 是 NVIDIA 的并行计算平台和编程模型&#xff0c;允许开发者直接利用 GPU 的并行计算能力&#xff0c;加速通用计算任…

【架构】微内核架构(Microkernel Architecture)

微内核架构(Microkernel Architecture) 核心思想 微内核架构(又称插件式架构)通过最小化核心系统,将可扩展功能以插件模块形式动态加载,实现高内聚低耦合。其核心设计原则: 核心最小化:仅封装基础通用能力(如插件管理、通信机制、安全校验)功能插件化:所有业务功能…

动静态链接与加载

目录 静态链接 ELF加载与进程地址空间&#xff08;静态链接&#xff09; 动态链接与动态库加载 GOT表 静态链接 对于多个.o文件在没有链接之前互相是不知到对方存在的&#xff0c;也就是说这个.o文件中调用函数的的跳转地址都会被设定为0&#xff08;当然这个函数是在其他.…

83_CentOS7通过yum无法安装软件问题解决方案

大家好,我是袁庭新。很多小伙伴在CentOS 7中使用yum命令安装软件时,出现无法安装成功的问题,今天给大家分享一套解决方案~ 在CentOS 7中,yum是一个常用的包管理工具,它基于RPM包管理系统。如果你发现yum无法使用,可能是由于多种原因造成的。以下是一些解决步骤,可以帮…

数据包在客户端和服务端,以及网络设备间如何传输的?

声明&#xff1a;文章中图片来自于网络收集&#xff0c;整体流程自己梳理。 目录 问题&#xff1a;如下socket客户端请求数据包如何传输的&#xff1f; 拓扑环境 数据包在分层间传输 网络分层L2/L3/L4 数据包收发-在各分层间变化 各层头部中-核心信息 数据包在不同设备…

用Python实现Excel数据同步到飞书文档

目录 一、整体目标 二、代码结构拆解 三、核心逻辑讲解&#xff08;重点&#xff09; 1. 建立安全连接&#xff08;获取access_token&#xff09; 2. 定位文档位置 3. 数据包装与投递 四、异常处理机制 五、函数讲解 get_access_token() 关键概念解释 1. 飞书API访问…

25林业研究生复试面试问题汇总 林业专业知识问题很全! 林业复试全流程攻略 林业考研复试真题汇总

25 林业考研复试&#xff0c;专业面试咋准备&#xff1f;学姐来支招&#xff01; 宝子们&#xff0c;一提到林业考研复试面试&#xff0c;是不是就慌得不行&#xff0c;感觉老师会扔出一堆超难的问题&#xff1f;别怕别怕&#xff0c;其实林业考研复试就那么些套路&#xff0c;…

vxe-table实现动态列

vxe-table实现动态列 1.动态列解释2.解决步骤2.1将后端返回的动态列表头&#xff0c;按照格式拼接在固定列表头上2.2将后端返回的列表数据按照键值对格式组装 1.动态列解释 正常列表是有固定的列&#xff1b;我的需求是&#xff0c;最初只知道表格的固定两列&#xff0c;查询数…

尚硅谷爬虫note009

一、jsonpath 1.安装 pip install jsonpath 2.使用 只能解析本地文件 .json文件 {"store": {"book": [{"category": "reference","author": "Nigel Rees","title": "Sayings of the Century&qu…

verilog笔记

Verilog学习笔记&#xff08;一&#xff09;入门和基础语法BY电棍233 由于某些不可抗拒的因素和各种的特殊原因&#xff0c;主要是因为我是微电子专业的&#xff0c;我需要去学习一门名为verilog的硬件解释语言&#xff0c;由于我是在某西部地区的神秘大学上学&#xff0c;这所…

Java+SpringBoot+Vue+数据可视化的综合健身管理平台(程序+论文+讲解+安装+调试+售后)

感兴趣的可以先收藏起来&#xff0c;还有大家在毕设选题&#xff0c;项目以及论文编写等相关问题都可以给我留言咨询&#xff0c;我会一一回复&#xff0c;希望帮助更多的人。 系统介绍 在当今社会&#xff0c;随着人们生活水平的不断提高和健康意识的日益增强&#xff0c;健…