当前位置: 首页 > news >正文

二分算法——优选算法

个人主页:敲上瘾-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;
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • [Python学习日记-26] Python 中的文件操作
  • 数据结构-树(基础,分类,遍历)
  • 黑马智数Day1
  • C++——将数组a[5]={-1,2,9,-5,7}中小于0的元素置成0。并将其结果输出(要求:用数组名作为函数的参数来实现)
  • 【无人机设计与控制】 基于matlab的蚁群算法优化无人机uav巡检
  • 通信工程学习:什么是VLAN虚拟局域网
  • go语言 数组和切片
  • C 语言数据结构中的堆与栈:深入理解与应用
  • 文件上传、重定向、Gin路由
  • 感知算法引入时序模型的优势
  • chapter 12 Bandgap References
  • Linux(6)--CentOS目录
  • Android架构组件:MVVM模式的实战应用与数据绑定技巧
  • 【前端】ES6:Proxy代理和Reflect对象
  • 第五章 继承、多态、抽象类与接口 (4)
  • CNN 在图像分割中的简史:从 R-CNN 到 Mask R-CNN
  • docker-consul
  • ES学习笔记(12)--Symbol
  • javascript 总结(常用工具类的封装)
  • java架构面试锦集:开源框架+并发+数据结构+大企必备面试题
  • PHP 7 修改了什么呢 -- 2
  • SQLServer之索引简介
  • SwizzleMethod 黑魔法
  • VUE es6技巧写法(持续更新中~~~)
  • vue从创建到完整的饿了么(18)购物车详细信息的展示与删除
  • 纯 javascript 半自动式下滑一定高度,导航栏固定
  • 第2章 网络文档
  • 回顾2016
  • 回流、重绘及其优化
  • 极限编程 (Extreme Programming) - 发布计划 (Release Planning)
  • 区块链分支循环
  • 深入体验bash on windows,在windows上搭建原生的linux开发环境,酷!
  • 使用putty远程连接linux
  • 阿里云服务器购买完整流程
  • ​sqlite3 --- SQLite 数据库 DB-API 2.0 接口模块​
  • ​Z时代时尚SUV新宠:起亚赛图斯值不值得年轻人买?
  • ​软考-高级-信息系统项目管理师教程 第四版【第14章-项目沟通管理-思维导图】​
  • ​数据结构之初始二叉树(3)
  • # 利刃出鞘_Tomcat 核心原理解析(八)-- Tomcat 集群
  • #考研#计算机文化知识1(局域网及网络互联)
  • #我与Java虚拟机的故事#连载05:Java虚拟机的修炼之道
  • (env: Windows,mp,1.06.2308310; lib: 3.2.4) uniapp微信小程序
  • (Oracle)SQL优化基础(三):看懂执行计划顺序
  • (Python) SOAP Web Service (HTTP POST)
  • (附源码)springboot建达集团公司平台 毕业设计 141538
  • (七)理解angular中的module和injector,即依赖注入
  • (三分钟)速览传统边缘检测算子
  • (四)opengl函数加载和错误处理
  • (一)eclipse Dynamic web project 工程目录以及文件路径问题
  • (一)pytest自动化测试框架之生成测试报告(mac系统)
  • (一)Thymeleaf用法——Thymeleaf简介
  • (原創) 如何將struct塞進vector? (C/C++) (STL)
  • (终章)[图像识别]13.OpenCV案例 自定义训练集分类器物体检测
  • .a文件和.so文件
  • .NET Core SkiaSharp 替代 System.Drawing.Common 的一些用法