二分算法——优选算法

news2024/9/20 13:58:36

个人主页:敲上瘾-CSDN博客

个人专栏:游戏、数据结构、c语言基础、c++学习、算法

        本章我们来学习的是二分查找算法,二分算法的应用非常广泛,不仅限于数组查找,还可以用于解决各种搜索问题、查找极值问题等。在数据结构和算法中,它是基础且重要的算法之一。

接下来我准备了三个题来引出并学习该算法,最后来做总结。

目录

一、二分查找

1.题目解析

2.算法原理

3.代码编写

二、查找元素的首末位置

1.题目解析

2.算法原理

3.代码编写

三、寻找峰值

1.题目解析

2.算法原理

3.代码编写

四、总结


一、二分查找

1.题目解析

        该题目要求是给一个元素target然后在升序数组nums中找到该元素的下标,若不存在则返回-1。题目要求简单易懂就不再多讲,这里要注意两个点:(1)、数组是升序的。(2)、需要返回的是元素下标。

2.算法原理

        对于该题最容易想到的解法就是把数组从头到尾遍历一遍,时间复杂度为O(N)。

        解法二:因为这个数组是有序的所以我们可以用二分的思想,首先使用三个指针(这里指的是数组的下标)left,right,mid,其中left和right维护一段区间,把目标值锁定到[left,right]区间内。mid表示区间的中心下标,把区间一分为二。

        接下来锁定target的位置:如果target小于mid下标指向的元素,那么因为数组是升序,所以target一定在区间[left,mid]中,即right更新为mid,然后更新mid( mid=left+(right-left)/2 )。              如果target大于mid指向元素,那么target一定在区间[mid,right]中,即把left更新为mid,然后更新mid。重复操作直到left>right。

如下:

该算法的时间复杂度为logN,证明如下:

3.代码编写

class Solution 
{
public:
    int search(vector<int>& nums, int target)
    {
        int left=0,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 return mid;
        }
        return -1;
    }
};

二、查找元素的首末位置

1.题目解析

        该题的题目要求与上一题类似,都是在升序数组中查找元素,不过该题查找的是元素第一次出现的位置和最后一次出现的位置。这里还要求时间复杂度为O(logN),这里就很明显地提示了该题要使用二分算法。

2.算法原理

        同样的此题可以使用暴力枚举来解决,不过时间复杂度为O(N),根据上题的经验这里我们还是考虑使用二分法。不过也很容易发现把上面的算法原模原样搬过来是解决不了该道题的,我们只能找到target元素的是无法锁定它第一次出现的位置和最后一次出现的位置。我们可以用以下解法:

左边界查找:

        当mid指向的元素为target的时候,不用返回,而是让right更新为mid,mid指向元素大于target时同样更新right为mid,即可以合并为

        if(nums[mid]>=target)   right=mid;

当mid指向元素小于target时left更新为mid+1,重复该操作就可以把数组右边的target忽略,锁定最左边的target。这里做循环的时候需要注意循环条件不能是left<=rigth,如果是left<=right就可能使mid一直赋值给right从而进入死循环,所以我们使用left<right

右边界查找:

        当mid指向的元素为target的时候,不用返回,而是让left更新为mid,mid指向元素小于target时同样更新left为mid,即可以合并为

        if(nums[mid]<=target)   left=mid;

当mid指向元素大于target时right更新为mid-1,重复该操作就可以把数组左边的target忽略,锁定最右边的target。这里做循环的时候同样需要注意循环条件只能是left<rigth。

注意:当元素只有两个时使用mid=left+(right-left)/2计算的mid就是第一个元素,那么也就是mid==left,如果mid指向的元素为target时把mid赋值给left(即left=mid)相当于什么也没干,程序同样会进入死循环。所以我们使用mid=left+(right-left+1)/2计算mid。

        以上的两个查找法几乎可以解决所有的二分算法题,可以作为一个模板记忆,在总结部分我会给出模板。

3.代码编写

class Solution 
{
public:
    vector<int> searchRange(vector<int>& nums, int target) 
    {
        if(nums.size()==0) return {-1,-1};
        int left=0,right=nums.size()-1;
        while(left<right)
        {
            int mid=left+(right-left)/2;
            if(nums[mid]>=target) right=mid;
            else left=mid+1;
        }
        if(nums[left]!=target) return {-1,-1};
        int n=left;
        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 {n,left};
    }
};

三、寻找峰值

1.题目解析

该题要求我们返回任意峰值,比如把数组元素抽象成连续的数据如下:

需要返回以上红圈部分的任意一点, 也就是该元素至少要满足它左边的第一个元素和右边的第一个元素都要小于它。

注意:题目给的信息nums[-1]和nums[n]为负无穷,所以不要忽略以下情况:

2.算法原理

        这题同样可以使用暴力枚举,时间复杂度为O(N),该解法就不再多讲。

        这个题与上两个题有一个很大的区别是该数组并不是有序的,但是没关系该题同样可以使用二分的思想,注意很多人会误认为二分算法只能用在有序的数组中,而事实并不局限于此,只需要找到数据的二段性即可,如该题我们可以这样:

        该方法也就是上一题的右边界查找,当然也可以使用左边界查找的方法,但用朴素的二分查找解决不了该题。

3.代码编写

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

四、总结

1、二分算法并不局限于数组有序,只要找到数据的二段性即可使用二分算法。

2、二分算法模板

2.1.朴素二分模板

while(left<=right)
{
    int mid=left+(right-left)/2;
    if(...) left=mid+1;
    else if(...) right=mid-1;
    else ...
}

2.2.左边界查找模板

while(left<right)
{
    int mid=left+(right-left)/2;
    if(...) left=mid+1;
    else right=mid;
}

2.3.右边界查找模板

while(left<right)
{
    int mid=left+(right-left+1)/2;
    if(...) left=mid;
    else right=mid-1;
}

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

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

相关文章

无人机飞手培训机构组建及市场分析

飞手培训机构是专门为培养无人机飞行员&#xff08;飞手&#xff09;而设立的教育机构。这些机构通过提供专业的培训课程&#xff0c;帮助学员掌握无人机飞行技术、了解相关法规、提升实战能力&#xff0c;并最终获得相关证书&#xff0c;以便在航拍摄影、农业植保、物流配送、…

MS SQL Server 实战 排查多列之间的值是否重复

目录 需求 范例运行环境 数据样本设计 功能实现 上传EXCEL文件到数据库 SQL语句 小结 需求 在日常的应用中&#xff0c;排查列重复记录是经常遇到的一个问题&#xff0c;但某些需求下&#xff0c;需要我们排查一组列之间是否有重复值的情况。比如我们有一组题库数据&…

django实现开发、测试、生产环境配置区分

文章目录 一、为什么要区分开发 (dev)、测试 (test) 和生产 (prod) 环境二、django项目如何通过配置实现环境配置的区分1、针对不同的环境创建不同的设置文件settings.py2、在设置文件中根据需要进行配置区分3、根据不同的环境运行使用不同的设置文件 任何实际的软件项目中都要…

使用Python打造全自动wx好友添加器:批量操作,高效省时的社交神器

在现代的数字营销和社交扩展中&#xff0c;自动化操作可以显著提高效率。尤其是wx这种广泛使用的即时通讯工具&#xff0c;很多用户有批量添加好友的需求&#xff0c;但手动操作费时费力。本教程将详细介绍如何使用 Python 开发一个自动化工具&#xff0c;帮助你批量添加wx好友…

宏任务和微任务+超全面试真题(持续更新ing

概念 微任务和宏任务是在异步编程中经常使用的概念&#xff0c;用于管理任务的执行顺序和优先级。 宏任务&#xff1a;setTimeout, setInterval&#xff0c;I/O 操作和 UI 渲染等。微任务&#xff1a; Promise 回调、async/await等 微任务通常比宏任务具有更高的优先级。 执…

C#基础(11)函数重载

前言 前面我们已经完成了ref和out补充知识点的学习&#xff0c;以及函数参数相关的学习&#xff0c;今天便再次为函数补充一个知识点&#xff1a;函数重载。 函数重载是指在同一个作用域中&#xff0c;可以有多个同名函数&#xff0c;但参数列表不同。它的发展可以追溯到早期…

【Chrome】开发一个Chrome扩展以及常见问题的解决方案

前言 本文介绍开发chrome扩展很重要的几种操作&#xff0c;如&#xff1a;操作网页dom、发送请求、渲染弹层、不同沙盒环境的通信方式、扩展与网页的通信方式、遇到iframe时的操作等。最终会提供一个简单的案例&#xff0c;其中涵盖了上述操作。 还有一些本人相关文章&#x…

HashMap 详解

哈希表 哈希表又叫散列表&#xff0c;或者映射、字典都是指哈希表&#xff0c;哈希表是通过关键码映射到数组的某个位置来访问的数据结构&#xff0c;实现这个映射的函数就是哈希函数&#xff0c;哈希表结合了数组和链表的优点&#xff0c;查找和插入操作的时间复杂度都是O(1)。…

MySQL篇(高级字符串函数/正则表达式)(持续更新迭代)

目录 讲点一&#xff1a;高级字符串函数 一、简介 二、常见字符串函数 1. CONCAT() 2. SUBSTRING() 3. LENGTH() 4. REPLACE() 5. TRIM() 6. UPPER() 7. LOWER() 8. LEFT() 9. RIGHT() 10. INSTR() 11. LENTH(str) 讲点二&#xff1a;正则表达式 一、简介 二、…

AIGC实战——多模态模型Flamingo

AIGC实战——多模态模型Flamingo 0. 前言1. Flamingo 架构2. 视觉编码器3. Perceiver 重采样器4. 语言模型5. FIamingo 应用小结系列链接 0. 前言 我们已经学习了文本生成图像模型 DALL.E 2&#xff0c;在本节中&#xff0c;我们将探索另一种多模态模型 Flamingo&#xff0c;它…

学习使用在windows系统上安装nodejs以及环境配置图文教程整理

学习使用在windows系统上安装nodejs以及环境配置图文教程整理 Node.js 介绍Node.js 安装1、Node.js下载2、Node.js安装3、Node.js测试4、Node.js安装目录5、Node.js环境变量配置6、配置镜像站&#xff0c;提升速度7、检查镜像站配置8、测试环境变量是否生效9、安装cnpm Node.js…

jwt报错,位置:找不到符号 parseClaimsJws(java.lang.String)

报错显示如图 报错信息为&#xff1a; E:\idea\project\tlias\src\main\java\org\itheima\tlias\utils\JwtUtils.java:36:17 java: 找不到符号 符号: 方法 parseClaimsJws(java.lang.String) 位置: 接口 io.jsonwebtoken.JwtParserBuilder 解决办法 项目使用的是最新…

p12docker 进入容器的命令和拷贝的命令

进入当前正在运行的容器 第一种方式是执行docker exec -it 8d57ffda7a29 /bin/bash这个时候可以根据docker容器的id进入到指定id的容器当中***(这个是比较常用的)*** 老师的笔记 第二种方式是docker attach 8d57ffda7a29 这里还是直接引用老师的笔记吧 从容器内部拷贝文…

HAL库学习梳理——GPIO

笔者跟着B站铁头山羊视频学习 STM32-HAL库 开发教程。有一说一&#xff0c;这个教程自诩为“最佳教程&#xff0c;没有之一~”&#xff0c;确实有点东西。像我这种看视频想睡觉的入门小白来说&#xff0c;感觉捡到宝了。下面对这些课程的应用做一个梳理。 省流&#xff1a; HA…

2-3.Android 存储之存储空间(私有空间、公共空间)

一、内部存储与外部存储 内部存储指位于设备的内部存储空间 外部存储指位于设备的外部存储介质&#xff0c;例如&#xff0c;SD 卡 简单理解&#xff0c;内部存储就是存储在手机自身&#xff0c;外部存储就是存储在手机可以外接的东西&#xff0c;好比电脑的硬盘和 U 盘 二、…

7-1 两个有序链表序列的交集

已知两个非降序链表序列S1与S2&#xff0c;设计函数构造出S1与S2的交集新链表S3。 输入格式: 输入分两行&#xff0c;分别在每行给出由若干个正整数构成的非降序序列&#xff0c;用−1表示序列的结尾&#xff08;−1不属于这个序列&#xff09;。数字用空格间隔。 输出格式:…

『功能项目』切换职业技能面板【49】

我们打开上一篇48切换职业面板的项目&#xff0c; 本章要做的事情是制作第二职业法师技能面板、第三职业面板并且完成切换 双击打开Canvas进入预制体空间 复制三个技能栏面板 重命名 设置第一技能栏 设置第二职业技能栏 设置第三职业技能栏 修改脚本&#xff1a;ChangeProfess…

TS.38-2

2.4.4 空闲模式 如果设备在接收DISPLAY TEXT STK主动命令时处于空闲模式&#xff0c;设备必须从这种待机模式切换到显示相关弹出窗口。一旦STK命令处理完成&#xff08;例如用户选择了“确定”按钮&#xff09;&#xff0c;设备必须返回到其初始状态。 2.4.5 锁屏 - 手动解锁…

Spark实操学习

Spark学习 一、Spark-Shell编程1. 配置python3(三台服务器都要配置)2. 开始Spark编程3. spark-shell工具 二、Java项目测试1. 新建项目2. Spark-java代码测试 三、Scala项目测试1. 安装scala2. 安装包管理器sbt3. 在编译工具中安装scala工具4. 新建项目5. spark-scala代码测试 …

1.MySQL在Centos 7环境安装

目录 MYSQL在Centos7环境安装MySQL环境配置清理环境检查安装包获取官方yum源安装mysql服务配置my.cnf设置开机启动 MYSQL在Centos7环境安装 MySQL环境配置 安装的时候&#xff0c;用户切换成root。初期练习&#xff0c;mysql不进行用户管理&#xff0c;全部使用root进行&#…