使用二分查找优化时间复杂度

        二分查找,也称为折半查找,是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。我们应该如何用在具体问题中呢?

题目链接(力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台)

 

可以直接通过遍历一次数组就得到对应值下标了,时间复杂度为 \Theta (n)。但是我们可以通过划分区间的方式获得更优秀的时间复杂度(二分查找),通过取中值的方式可以把一个数组划分出两个区间,通过中值可以判断出target在哪一个区间,循环迭代即可。按照这种方式我们可以一次舍去一整片区间,相比于遍历数组一次舍去一个数值,得到的时间复杂度为 \Theta (logn)
具体细节见代码:
int search(vector<int>& nums, int target) {
    int left = 0, right = nums.size()-1;
    while(left <= right){
        int mid = (left + right)/2;
        if(nums[mid] > target)  //要查找的目标值在左区间
            right = mid - 1;
        else if(nums[mid] < target)  //要查找的目标值在右区间
            left = mid + 1;
        else
            return mid;
    }
    return -1;
}

题目链接(力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 )

 

我们依旧可以通过一次遍历完成,定义两个指针分别记住target值的第一次出现的下标,第二个指针记住target值最后一次出现的下标即可,时间复杂度为 \Theta (n)。题目要求时间复杂度为 \Theta (logn),我们采用上面这种二分查找算法会有一些问题,因为如果我们有一个数组 nums = [5, 5, 5, 5, 5, 5 ,5], target = 5 ,我们获得的mid既不知道开头下标也不知道结尾下标。也就是说,当mid等于target值的时候,我们需要额外做一些事情。
具体细节见代码:
vector<int> searchRange(vector<int>& nums, int target) {
    if(nums.size() == 0)
        return {-1, -1};

    int begin = 0, end = 0;
    //处理begin
    int left = 0, right = nums.size()-1;
    while(left < right){
        int mid = (left + right)/2;
        if(nums[mid] < target)  //目标值在右区间
            left = mid + 1;
        else
            right = mid;
    }
    if(nums[left] != target)
        return {-1, -1};
    else
        begin = left;
        
    //处理end
    left = 0, right = nums.size()-1;
    while(left < right){
        int mid = (left + right + 1)/2;
        if(nums[mid] <= target)  //目标值在右区间
            left = mid;
        else
            right = mid - 1;
    }
    end = right;
    return {begin, end};
}

所以不管是区间还是单个元素的问题,我们可以总结一个通用的模版:

处理区间左端点:

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

        通过 nums[mid] < target 这个条件来不断逼近目标值区间的左端点,最终left和right一起指向目标值区间的左端点。

处理区间右端点:

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

        通过 nums[mid] > target 这个条件来不断逼近目标值区间的右端点,最终left和right一起指向目标值区间的右端点。

注意:
1. 为什么在while循环的时候我们判断条件使用的是 left < right 而不是 left <= right 呢?
        以处理目标值区间的左端点为例:
        根据if else里的条件,left只会在target的左边的区间内移动,不会越过target,也就是说left可以移动到的最右边的位置正好是target的开始位置(如果符合条件的target只有一个,则就是这个元素的位置);
        right同理,也会在右边的区间移动,不会越过target,right可以移动到的最左边的位置正好是target的结束位置(如果符合条件的target只有一个,则就是这个元素的位置);
        根据以上两点分析,当 left = right 的时候一定是最终结果,要么是target的位置,要么就是无解。如果我们使用的是 left <= right ,当 left = right 的时候还会继续进入循环,循环没有突破边界的可能(即不可能出现left > right的可能),程序就会死循环。
2. 在求中点的时候要向下取整,避免在极端情况下(如果向上取整的话,如果此刻三个下标的位置是nums = [..., left, mid 和 right, ... ],如果下一次循环依旧是更新right的话,三个下标的位置还是nums = [..., left, mid 和 right, ... ])程序进入死循环。同理,在处理右端点的时候需要向上取整。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux操作系统基础(十二):yum软件包管理器

文章目录 yum软件包管理器 一、yum常用命令 二、yum在线安装软件案例 三、yum在线删除软件案例 yum软件包管理器 yum&#xff08; Yellow dog Updater, Modified&#xff09;是一个在 Fedora 和 RedHat中的 Shell 前端软件包管理器。基于RPM包管理&#xff0c;能够从指定的…

高效的工作学习方法

1.康奈尔笔记法 在这里插入图片描述 2. 5W2H法 3. 鱼骨图分析法 4.麦肯锡7步分析法 5.使用TODOLIST 6.使用计划模板&#xff08;年月周&#xff09; 7. 高效的学习方法 成年人的学习特点&#xff1a; 快速了解一个领域方法 沉浸式学习方法&#xff1a; 沉浸学习的判据&am…

matplotlib从起点出发(13)_Tutorial_13_Autoscaling

0 自动放缩 轴上的限制可以手动设置&#xff08;例如ax.set_xlim(xmin, xmax))&#xff0c;或者Matplotlib可以根据Axes上已有的数据自动设置它们。此种放缩行为有许多选项&#xff0c;如下所述。 我们将从一个简单的折线图开始&#xff0c;显示自动缩放将轴限制扩展到数据的…

如何生成生成一个修仙世界的狗血短剧剧本

如何生成生成一个修仙世界的狗血短剧剧本 生成一个修仙世界的狗血短剧剧本将上述剧本转为对话 生成一个修仙世界的狗血短剧剧本 剧本名称&#xff1a;《仙途情缘》 角色&#xff1a; 易天行&#xff1a;男主角&#xff0c;天赋异禀的修仙者&#xff0c;性格坚毅&#xff0c;正…

鸿蒙开发系列教程(十九)--页面内动画(2)

组件内转场动画 组件的插入、删除过程即为组件本身的转场过程&#xff0c;组件的插入、删除动画称为组件内转场动画。通过组件内转场动画&#xff0c;可定义组件出现、消失的效果。 transition(value: TransitionOptions) 参数可以定义平移、透明度、旋转、缩放这几种转场样…

rabbitmq自用记录

参考博客RabbitMq安装与使用&#xff08;mac&#xff09;高效总结&#xff08;亲测&#xff09;_mac 安装rabbitmq 服务端口-CSDN博客 启动服务 这里提前把redis服务也启动了 这里看到前端更改数据,后端进行日志打印 登录后访问rabbitmq网址

docker数据科学与spark镜像源与使用常见问题疑难解答

以下是一些与数据挖掘和数据科学相关的 Docker 镜像源&#xff1a; jupyter/all-spark-notebook: 此镜像包含 Jupyter Notebook 和 Spark 的完整环境&#xff0c;用于 Spark 开发和学习。 rocker/tidyverse: 此镜像包含用于 R 语言的 tidyverse 数据科学包。 jupyter/scipy-n…

代码随想录算法训练营Day25|回溯算法·组合总和III,电话号码的字母组合

组合总和III 题目&#xff1a;找出所有相加之和为n的k个数的组合。组合中只允许含有1-9的正整数&#xff0c;并且每种组合中不存在重复的数字。 组合变量个数为k个&#xff0c;和为n。简单思路是使用k重循环&#xff0c;一层层找出来&#xff0c;然后把每一层的数相加&#x…

单链表的介绍

一.单链表的概念及结构 概念&#xff1a;链表是⼀种物理存储结构上⾮连续、⾮顺序的存储结构&#xff0c;数据元素的逻辑顺序是通过链表 中的指针链接次序实现的 。 结构&#xff1a;根据个人理解&#xff0c;链表的结构就像火车厢一样&#xff0c;一节一节连在一起的&#x…

浅析Linux追踪技术之ftrace:Event Tracing

文章目录 概述使用Event Tracing使用set_event接口使用enable接口 Event配置Event formatEvent Filtering过滤规则设置过滤器 Event TriggerTrigger语法 Trace marker相关参考 概述 Event Tracing&#xff08;事件追踪&#xff09;利用在内核代码中加入的各种Tracepoint&#…

计算机网络概述习题拾遗

学习目标&#xff1a; 自下而上第一个提供端到端服务的层次 路由器、交换机、集线器实现的功能层 TCP/IP体系结构的网络接口层对应OSI体系结构的哪两个层次 分组数量对总时延的影响 如果这篇文章对您有帮助&#xff0c;麻烦点赞关注支持一下动力猿吧&#xff01; 学习内容…

Hive的Join连接、谓词下推

前言 Hive-3.1.2版本支持6种join语法。分别是&#xff1a;inner join&#xff08;内连接&#xff09;、left join&#xff08;左连接&#xff09;、right join&#xff08;右连接&#xff09;、full outer join&#xff08;全外连接&#xff09;、left semi join&#xff08;左…

课时27:内容格式化_输入格式化_EOF原理

3.2.1 EOF原理 学习目标 这一节&#xff0c;我们从 基础知识、简单实践、小结 三个方面来学习。 基础知识 场景需求 在运维岗位中&#xff0c;有非常多的场景需要我们在脚本中编写应用软件的配置文件。在这些应用软件的配置文件中&#xff0c;经常携带大量的格式&#xff0…

第四篇【传奇开心果微博系列】Python微项目技术点案例示例:美女颜值判官

传奇开心果微博系列 系列微博目录Python微项目技术点案例示例系列 微博目录一、微项目目标二、雏形示例代码三、扩展思路四、添加不同类型的美女示例代码五、增加难度等级示例代码六、添加特殊道具示例代码七、设计关卡系统示例代码八、添加音效和背景音乐示例代码九、多人游戏…

一文看懂春晚刘谦魔术

魔术步骤 step1: 准备4张牌,跟随魔术步骤,见证奇迹 step2: 将4张牌平均斯成两份,并叠在一起 step3: 将牌堆顶数量为名字字数的牌移到牌堆底 step4: 将前三张牌放在牌堆中间并取出牌堆顶的一张牌放到屁股下 step5: 南方人、北方人、不确定分别取顶上的1/2/3张牌插入牌堆…

Stable Diffusion主流UI详细介绍

Stable Diffusion目前主流的操作界面有WebUI、ComfyUI以及Fooocus 这里webui和fooocus在人机交互上的逻辑是一样的&#xff0c;fooocus界面更加简洁。 comfyui是在人机交互上是采用流程节点的交互逻辑&#xff0c;和上面略有区别。 界面分别如下&#xff1a; WebUI界面如下 we…

C/C++内存管理:new、delete功能及原理实现

目录 一、C/C内存分布 二、C中内存管理方式 2.1new/delete操作内置类型 2.2 new和delete操作自定义类型 三、operator new与operator delete函数 四、new和delete的实现原理 4.1内置类型 4.2自定义类型 五、定位new 一、C/C内存分布 int globalVar 1; static int sta…

HarmonyOS鸿蒙学习基础篇 - Column/Row 组件

前言 Row和Column组件是线性布局容器&#xff0c;用于按照垂直或水平方向排列子组件。Row表示沿水平方向布局的容器&#xff0c;而Column表示沿垂直方向布局的容器。这些容器具有许多属性和方法&#xff0c;可以方便地管理子组件的位置、大小、间距和对齐方式。例如&#xff0c…

四、案例 - Oracle数据迁移至MySQL

Oracle数据迁移至MySQL 一、生成测试数据表和数据1.在Oracle创建数据表和数据2.在MySQL创建数据表 二、生成模板文件1.模板文件内容2.模板文件参数详解2.1 全局设置2.2 数据读取&#xff08;Reader&#xff09;2.3 数据写入&#xff08;Writer&#xff09;2.4 性能设置 三、案例…

博主用树莓派绕过 Windows Bitlocker 加密,用时不到一分钟

近日 YouTube 博主 stacksmashing 发现 Bitlocker 存在一个巨大的安全漏洞&#xff0c;他利用价值不到 10 美元的树莓派 Pico 在不到一分钟内成功绕过了该加密。 2 月 7 日消息&#xff0c;微软 Windows 10 和 11 专业版内置的 Bitlocker 加密功能一直被认为是方便易用的安全解…