二分查找算法(2) _在排序数组中查找元素的第一个和最后一个_模板

news2024/9/23 3:20:09

个人主页:C++忠实粉丝
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C++忠实粉丝 原创

二分查找算法(2) _在排序数组中查找元素的第一个和最后一个_模板

收录于专栏【经典算法练习】
本专栏旨在分享学习算法的一点学习笔记,欢迎大家在评论区交流讨论💌

目录

1. 题目链接:

2. 题目描述 :

3. 解法 :

    解法一:暴力枚举遍历:

    算法思路 :

    代码展示 :

    结果分析 :

    解法二: 二分查找 :

    算法思路 :

    细节处理:

查找区间的左端点

1. 循环条件

2. 求终点的操作 

 查找区间的右端点 

1. 循环条件

2. 求终点的操作 

    代码展示 :

    结果分析 :

4. 二分算法模板总结


1. 题目链接:

OJ链接: 在排序数组中查找元素的第一个和最后一个

2. 题目描述 :

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

3. 解法 :

    解法一:暴力枚举遍历:

    算法思路 :

使用两个头尾指针遍历整个数组,在排序数组中查找元素的第一个和最后一个

    代码展示 :

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int left = 0, right = nums.size() - 1;
        while(left < right)
        {
            if(nums[left] < target) left++;
            else if(nums[right] > target) right--;
            else break;
        }
        if(left <= right && nums[left] == target && nums[right] == target)
            return {left, right};
        else return{-1, -1};
    }
};

 

    结果分析 :

题目顺利通过!时间复杂度为: O(N),不过算法还有进一步优化的空间.

    解法二: 二分查找 :

    算法思路 :

a.分析插⼊位置左右两侧区间上元素的特点:
设插⼊位置的坐标为 index ,根据插⼊位置的特点可以知道:
        •[left, index - 1] 内的所有元素均是⼩于 target 的;
        •[index, right] 内的所有元素均是⼤于等于 target 的。

b.设 left 为本轮查询的左边界, right 为本轮查询的右边界。根据 mid 位置元素的信
息,分析下⼀轮查询的区间:
        ▪ 当 nums[mid] >= target 时,说明 mid 落在了[index, right] 区间上,
        mid 左边包括 mid 本⾝,可能是最终结果,所以我们接下来查找的区间在[left,
        mid] 上。因此,更新 right 到 mid 位置,继续查找。
        ▪ 当 nums[mid] < target 时,说明 mid 落在了[left, index - 1] 区间上,
       mid 右边但不包括 mid 本⾝,可能是最终结果,所以我们接下来查找的区间在[mid
        + 1, right] 上。因此,更新 left 到 mid + 1 的位置,继续查找。

c.直到我们的查找区间的⻓度变为 1 ,也就是 left == right 的时候, left 或者
    right 所在的位置就是我们要找的结果。

总结:

查找区间左端点 --- [..., target) [target, ...]

1. nums[mid] < t left  = mid + 1

2. nums[mid] >= t right = mid

 

 查找区间右端点 --- [..., target] (target, ...]

1. nums[mid] <= t left  = mid

2. nums[mid] > t right = mid - 1

 

    细节处理:

查找区间的左端点
1. 循环条件

while(left < right)

注意这里不能使用while(left <= right) 

1. 有结果: 当我们的left == right时,我们的left即是我们的结果,但如果你是使用while(left <= right) 循环,程序会陷入死循环,因为此时right + left / 2 = right/left

2. 全部大于target: 那我们的right会一直往左走,直到left与right相遇,这时候我们只需要判断它们相遇点的值是否等于target就行,相反使用while(left <= right)循环,程序依旧陷入死循环

3. 全部小于target: 那我们的left会一直往右走,与情况2是一样的

2. 求终点的操作 

left + (right - left) / 2

注意这里不能使用left + (right - left + 1) / 2

当我们判断只剩下两个元素时,left指向第一个元素, right指向第二个元素

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

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

        当nums[mid]  < target

                1: left = mid + 1 =right 循环成功结束

                2: left = mid + 1 = right + 1成功错过

        当nums[mid] >= target 

                1: right = mid right == left 循环成功结束

                2: right = mid 死循环

 查找区间的右端点 
1. 循环条件

while(left < right)

 原理和查找区间的左端点的一样

2. 求终点的操作 

left + (right - left + 1) / 2 

注意这里不能使用left + (right - left) / 2

当我们判断只剩下两个元素时,left指向第一个元素, right指向第二个元素

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

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

        当nums[mid]  <= target

                1: left = mid  死循环

                2: left = mid left== right + 1 循环成功结束

        当nums[mid] > target 

                1: right = mid - 1 right == left - 1循环成功错过

                2: right = mid - 1 right == left 循环成功结束

    代码展示 :

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        if(nums.size() == 0) return {-1, -1};
        //找左端点 --- [..., target) [target, ...] 
        int left = 0, right = nums.size() - 1, begin = 0;
        while(left < right)
        {
            int mid = left + (right - left) / 2;
            if(nums[mid] >= target)  right = mid;
            else left = mid + 1;
        }
        if(nums[left] == target) begin = left;
        else return {-1, -1};

        //找右端点 --- [..., target] (target, ...]
        left = 0, right = nums.size() - 1;
        while(left < right)
        {
            int mid = left + (right - left + 1) / 2;
            if(nums[mid] <= target) left = mid;
            else right = mid - 1;  
        }
        return {begin, right};
    }
};

 

    结果分析 :

这里一定要注意细节问题,不然程序很容易死循环

4. 二分算法模板总结

while(left < right)

{

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

        if(...) left = mid + 1;

        else right = mid;
}

while(left < right)

{

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

        if(...) left = mid;

        else right = mid - 1;
}

快速记忆:

分类讨论的代码,就题论题即可

下面出现-1,上面就+1,否侧不加 

 

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

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

相关文章

算法-K个一组翻转链表

// 要实现没k个节点进行翻转的操作&#xff0c;可以按照一下步骤进行 // 1.计算链表长度 // 2.分组反转 // 3. 使用一个虚拟头节点来处理边界情况 // 4.每次处理k个节点进行反转 // 5.如果剩余节点不足k个 则保持原有顺序 // 6.依次反转每组中的节点 // 1.使用prevGroupEEnd追…

EvilScience靶机详解

主机发现 arp-scan -l 得到靶机ip 192.168.229.152 端口扫描 nmap -sV -A -T4 192.168.1.20 这段代码使用 nmap 命令来扫描目标主机 192.168.1.20&#xff0c;并执行以下操作&#xff1a;-sV&#xff1a;探测开放的端口&#xff0c;以确定服务/版本信息。-A&#xff1a;启…

[大语言模型] LINFUSION:1个GPU,1分钟,16K图像

1. 文章 2409.02097 (arxiv.org)https://arxiv.org/pdf/2409.02097 LINFUSION: 1 GPU, 1 MINUTE, 16K IMAGE 摘要 本文介绍了一种新型的扩散模型LINFUSION&#xff0c;它能够在保持高分辨率图像生成性能的同时显著降低时间和内存复杂度。该模型采用了基于Transformer的UNet进…

常用卫星学习

文章目录 Landsat-8 Landsat-8 由一台操作陆地成像仪 &#xff08;OLI&#xff09; 和一台热红外传感器 &#xff08;TIRS&#xff09;的卫星&#xff0c;OLI 提供 9 个波段&#xff0c;覆盖 0.43–2.29 μm 的波长&#xff0c;其中全色波段&#xff08;一般指0.5μm到0.75μm左…

Java的IO流(二)

目录 Java的IO流&#xff08;二&#xff09; 字节缓冲流 基本使用 使用缓冲流复制文件 字符缓冲流 缓冲流读取数据原理 字符编码 字符集 转换流 序列化流与反序列化流 基本使用 禁止成员被序列化 序列号不匹配异常 打印流 基本使用 系统打印流与改变流向 Prop…

【kaggle竞赛】毒蘑菇的二元预测题目相关信息和思路求解代码

毒蘑菇的二元预测 您提供了很多关于不同二元分类任务的资源和链接&#xff0c;看起来这些都是Kaggle竞赛中的参考资料和高分解决方案。为了帮助您更好地利用这些资源&#xff0c;这里是一些关键点的总结&#xff1a; Playground Season 4 Episode 8 主要关注的竞赛: 使用银行…

2024 硬盘格式恢复软件大揭秘

宝妈们硬盘存储图片、设计师用硬盘存储素材、学生们用硬盘存储作业和数据已经是一个普遍的社会现象了。但是有时候数据迁移之后想要一份全新的硬盘我们就会采取硬盘格式化的操作&#xff0c;如果格式化之后发现硬盘数据没有备份好硬盘格式化后能恢复数据吗&#xff1f;这次我就…

没错,我给androidx修了一个bug!

不容易啊&#xff0c;必须先截图留恋&#x1f601; 这个bug是发生在xml中给AppcompatTextView设置textFontWeight&#xff0c;但是却无法生效。修复bug的代码也很简单&#xff0c;总共就几行代码&#xff0c;但是在找引起这个bug的原因和后面给androidx提pr却花了很久。 //App…

git学习【完结】

git学习【完结】 文章目录 git学习【完结】一、Git基本操作1.创建本地仓库2.配置本地仓库1.局部配置2.全局配置 3.认识工作区、暂存区、版本库4.添加文件5.修改文件6.版本回退7.撤销修改8.删除文件 二、Git分支管理1.理解分支2.创建、切换、合并分支3.删除分支4.合并冲突5.合并…

【每天学个新注解】Day 2 Lombok注解简解(一)—@Data、@Build、@Value

Data 相当于同时使用了 Getter 、Setter 、RequiredArgsConstructor、ToString、EqualsAndHashCode 1、如何使用 需要同时使用Getter 、Setter 、RequiredArgsConstructor、ToString、EqualsAndHashCode注解一个Bean的时候。 2、代码示例 例&#xff1a; Data public cla…

H5白色大方图形ui设计公司网站HTML模板源码

源码名称&#xff1a;白色大方图形ui设计公司网站模板源码 源码介绍&#xff1a;一款H5自适应白色大方图形ui设计公司官网网站模板源码。源码含有七个页面&#xff0c;可用于各种设计公司官网。 需求环境&#xff1a;H5 下载地址&#xff1a; https://www.51888w.com/369.ht…

基于vue框架的宠物托管系统设计与实现is203(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;用户,宠物种类,商家,咨询商家,用户宠物,宠物托管,宠物状况,宠物用品,用品分类,商家公告,结束托管,账单信息,延长托管 开题报告内容 基于Vue框架的宠物托管系统设计与实现开题报告 一、引言 随着现代生活节奏的加快&#xff0c;越来越…

如何在Linux Centos7系统中挂载群晖共享文件夹

前景&#xff1a;企业信息化各种系统需要上传很多的图片或者是文件&#xff0c;文件如何在群晖中显示&#xff0c;当文件或者图片上传到linux指定文件夹内&#xff0c;而文件夹又与群晖共享文件夹进行挂载&#xff0c;就能保证上传的文件或者图片出现在群晖并在群晖里进行管理。…

分布式安装LNMP

目录 搭建LNMP架构 安装mysql 1.上传mysql软件包&#xff0c;关闭防火墙和核心防护 2.安装环境依赖包&#xff0c;桌面安装可能有自带的数据库除 3.配置软件模块 4.编译及安装 5.创建mysql用户 6.修改mysql 配置文件 7.更改mysql安装目录和配置文件的属主属组 8.设置…

Rumor Mitigation in Social Media Platforms with Deep Reinforcement Learning

ABSTRACT 社交媒体平台已成为人们传播和获取信息的主要渠道之一&#xff0c;其可靠性受到网络谣言的严重威胁。现有的辟谣手段如暂停用户、播放真实信息等&#xff0c;要么成本高&#xff0c;要么扰乱用户。在本文中&#xff0c;我们引入了一种新颖的谣言缓解范例&#xff0c;…

springboot每次都需要重设密码?明明在springboot的配置中设置了密码

第一步&#xff1a;查看当前的密码是什么&#xff1f; 打开redis-cli.exe&#xff0c;输入config get requirepass&#xff0c;查看当前的密码是什么&#xff1f; 接着&#xff0c;修改redis的配置文件&#xff0c;找到redis的安装目录&#xff0c;找到相关的conf文件&#x…

Spring高手之路24——事务类型及传播行为实战指南

文章目录 1. 编程式事务&#xff08;不推荐&#xff09;2. 声明式事务&#xff08;推荐&#xff09;3. 事务的传播行为&#xff08;复杂混合事务场景及时序图说明&#xff09;3.1 NESTED和REQUIRES_NEW传播行为的区别 1. 编程式事务&#xff08;不推荐&#xff09; 定义&#…

如何从 Nutanix 迁移至 SmartX 超融合?解读 4 类迁移方案和 2 例迁移实践

2022 年底&#xff0c;Nutanix&#xff08;路坦力&#xff09;正式宣布将中国市场交由合作伙伴&#xff08;联想&#xff09;主导销售&#xff0c;并于 2023 年 8 月完成全面转型。转型后&#xff0c;虽然中国用户依旧可以使用 Nutanix 产品&#xff0c;但在软件的续保和维保方…

企业EMS -能源管理系统-能源管理系统源码-能源在线监测平台

能源管理系统是以帮助工业生产企业在扩大生产的同时&#xff0c;合理计划和利用能源&#xff0c;降低单位产品能源消耗&#xff0c;提高经济效益&#xff0c;降低CO2排放量为目的信息化管控系统。 我国能源管理从上世纪80年代中期开始&#xff0c;通过“能量平衡测试”、“能源…

安卓数据存储——SharedPreferences

共享参数 SharedPreferences 1、sharedPreferences是Android的一个轻量级存储工具&#xff0c;采用的存储结构是key - value的键值对方式 2、共享参数的存储介质是符合XML规范的配置文件。保存路径是&#xff1a;/data/data/应用包名/shared_prefs/文件名.xml 使用场景&…