算法 || 二分查找

目录

二分查找

在排序数组中查找元素的第一个和最后一个位置

搜索插入位置 


 

一个数组经过划分后具有二段性的都可以用二分查找

二分查找

704. 二分查找 - 力扣(LeetCode)

暴力解法:直接遍历数组,找到 target 便返回下标,数组都遍历完了仍找不到,则返回 -1,时间复杂度为 O ( n )(最坏的情况:如果数组中不存在 target,则需要遍历整个数组)。

注意到题目中提供的数组是升序的,即数组从左往右是递增的。我们在数组中随意找个下标,设为 i,

1、如果nums[ i ] > target ,由于数组升序,说明 i 右边的数(即比 nums[ i ] 大的数)一定也大于 target,所以 target 应该落在 i 的左边,我们就不必遍历 i 右边的数了

2、同理,如果nums[ i ] < target ,由于数组升序,说明 i 左边的数(即比 nums[ i ] 小的数)一定也小于 target,所以 target 应该落在 i 的右边,我们就不必遍历 i 左边的数了

3、如果 nums[ i ] == target,那么 i 就是我们想要的返回值

 于是衍生出二分查找, 定义 left、right、mid,

1、如果 nums[ mid ] > target,mid 左边的数不必遍历了,所以 right = mid - 1

2、如果 nums[ mid ] < target,mid 右边的数不必遍历了,所以 left = mid + 1

3、如果 nums[ mid ] == target,说明找到了,返回 mid

4、如果 left > right,说明数组中不存在 target ,返回 -1

要注意 mid 的计算,防止溢出!!

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

在排序数组中查找元素的第一个和最后一个位置

34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/description/

 暴力解法:把数组从头到尾遍历一遍,并标记 target 第一次和最后一次出现的下标,时间复杂度为 O(n)。

注意到,题目提供的数组为非递减数组,即 nums[ i ] <= nums[ i+1 ]。

在分析问题之前,我们先区分下面两个 mid 的计算:

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

1、如果 nums[ left ] ~ nums[ right ] 中有奇数个数,mid 的两种计算方法没有区别,都会指向同一个下标;

2、如果 nums[ left ] ~ nums[ right ] 中有偶数个数,mid = left + ( right - left )/2 相当于向下取整,mid = left + ( right - left +1 )/2 相当于向上取整

比如下图,left = 0,right = 5,left + ( right - left )/2 = 2.5,但由于 mid 为 整型,最终 mid 为 2(相当于向下取整);mid = left + ( right - left +1 )/2 = 3(相当于对 2.5 向上取整)。

我们先找 target 第一次出现的下标 begin :begin 下标相当于把数组分为两段,下标 0 ~ begin -1 的数小于 target ,下标为 begin ~ nums.size( ) -1 的数大于等于 target

再找出 target 最后一次出现的下标 end :end 下标也把数组分为两段,下标 0 ~ end 的数小于等于 target,下标为 end+1 ~ nums.size( ) -1 的数大于 target

我们可以根据上面的二段性来找 target 第一个和最后一个位置。

先找 begin,left 从 0 开始,right 从 nums.size( ) -1 开始,

1、如果 nums[ mid ] < target,说明下标小于等于 mid 的数一定小于 target,所以 left = mid + 1

2、如果 nums[ mid ] >= target,说明下标大于 mid 的数一定大于  target,但是下标等于 mid 的数可能大于 target,也可能等于 target,所以 right = mid,如果 right = mid -1 ,那么 nums[ mid ] == target  的情况会被跳过,即可能是第一次出现的下标被跳过了;

3、在找 begin 时,mid = left + ( right - left )/2,因为 mid 向下取整才可以找出在一连串连续出现的  target 中找出第一次出现的下标(相当于整体都靠左边找)

4、left = right 时,while 循环结束,停止寻找,我们需要判断 while 循环结束时 nums[ left ] == target,因为数组中可能不存在 target,如果不存在,可以直接返回 -1 了,没有必要找最后一次出现的下标

 在找 end 位置时,也是相似的道理,

1、如果 nums[ mid ] <= target,说明下标小于等于 mid 的数一定小于等于 target,同样,考虑到下标为 mid 的数可能等于 target,所以 left = mid

2、如果 nums[ mid ] > target,说明下标大于等于 mid 的数一定大于  target,所以 right = mid -1

3、在找 end 时,mid = left + ( right - left +1 )/2,因为 mid 向上取整才可以找出在一连串连续出现的  target 中找出最后一次出现的下标(相当于整体都靠右边找)

TIP:如果  right 的计算中出现了 -1,那么 mid 的计算中就会出现 +1

 

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

搜索插入位置 

35. 搜索插入位置 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/search-insert-position/description/

 同样可以把数组根据 target 分为两段,一边小于 target,一边大于等于 target。

1、如果 nums[ mid ] < target,则 left = mid +1 ;

2、如果 nums[ mid ] >= target,则 right = mid

3、当 left = right 时,退出 while 循环,

a.如果 nums[ left ] < target,说明数组中不存在 target,我们需要把 target 插入到下标为 left +1 的位置中,返回 left +1 ;

b.如果 nums[ left ] > target,同样说明数组中不存在 target,需要把 target 插入到下标为 left 位置中,返回 left;

c.如果 nums[ left ] == target ,说明数组中存在 target,不需要插入,直接返回 left

class Solution {
public:int searchInsert(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) left=mid+1;else right=mid;}if(nums[left]<target) return left+1;//target比nums[left]大,则在left+1位置插入else return left;//target比nums[left]小,则在left位置插入,若相等,则返回在数组中的下标}
};

 

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于微信小程序云开发实现考研题库小程序V2.0

不久之前&#xff0c;基于云开发的微信答题小程序搭建题库小程序V1.0&#xff0c;软件架构是微信原生小程序云开发。现在来回顾一下&#xff0c;已经实现的功能。 一、V1.0项目预览 1、页面结构 首页 答题页 结果页 我的页 排行榜页 答题历史页 登录页 使用指引页 2…

LeetCode57. 插入区间

LeetCode57.插入区间 题目思路: 代码 /* 前置知识&#xff1a; vector<vector<int>> a,b; 二维vector数组是可以将二维中的一维vector数组给push_back的&#xff0c; 不是只有单个元素才可以&#xff0c;整个一维的vector数组也可以 b[0] {1,2,3},b[1] {4,5,6}…

如何消除浏览器SmartScreen对网站“不安全”提示?

面对互联网时代用户对网站安全性和可信度的严苛要求&#xff0c;网站运营者时常遭遇Microsoft Defender SmartScreen&#xff08;SmartScreen&#xff09;提示网站不安全的困扰。本文将剖析SmartScreen判定网站不安全的原因&#xff0c;并为运营者提供应对策略&#xff0c;以恢…

element 分页切换时:current-page无效 页数不会跟着一起切换

问题回溯&#xff1a;使用el-pagination组件 选择切换当前分页 页数为2 问题结果&#xff1a;el-pagination组件 当前页切换失败 一直都是 1&#xff0c;接口传参分页数据是2&#xff0c;打印当前分页也是2 解决方案1&#xff1a;使用 current-page参数 .sync 修饰符 解决方案2…

DM8达梦数据库模式下,备份库中具体某一张表SQL操作

1、进行达梦数据库的安装路径&#xff0c;我的默认安装路径为 /home/dmdba/dmdbms/bin 2、执行表备份的命令dexp ./dexp MO_FORM_V5/"MO_FORM_V5"192.168.179.11:5236 DIRECTORY/opt/dm_back/ FILEsystem.dmp TABLES"MO_FORM_V5.FORM_WARN_RULE" LOGs…

【问题实操】银河高级服务器操作系统实例分享,配置hugepages启动异常

1.问题现象 某运营商国产服务器操作系统项目&#xff0c;部署Kylin-Server-0524-aarch64服务器系统&#xff0c;内核从4.19.90-24.4升级到4.19.90-25.14。在grub中配置huagepages大页内存后&#xff0c;系统在内核启动阶段黑屏&#xff0c;只显示一个光标。grub配置如下图&…

商店数据(八)

目录 57.后台权限表 58.推荐记录表 ​59.系统上传资源表 ​60.角色表 ​61.订单结算表 62.店铺表 63.店铺认证表 64.店铺申请&#xff08;移动端&#xff09;表 57.后台权限表 CREATE TABLE wat_privilgeid (privilegeod int(11) NOT NULL AUTO_INCREMENT COMMENT 自增…

C#命名空间常用函数

在C#中&#xff0c;不同命名空间下有各种常用函数&#xff0c;下面列举一些常见的函数及其对应的命名空间&#xff1a; System命名空间&#xff1a; Console.WriteLine()&#xff1a;用于向控制台输出信息。Convert.ToInt32()&#xff1a;用于将其他数据类型转换为整数类型。 S…

数据结构--栈与队列【您的关注是我创作的动力!】

文章目录 栈什么是栈&#xff1f;栈的具体实现 队列什么是队列&#xff1f;队列的实现 栈 什么是栈&#xff1f; 栈也是顺序表的一种&#xff0c;栈的逻辑实现是先进后出&#xff08;后进先出&#xff09;就跟子弹夹一样。 具体逻辑就是它只允许在固定的一端进行数据的插入与…

小程序使用阿里巴巴矢量图标库

一、登录官网 www.iconfont.cn 二、在搜索框中搜索想要的图标&#xff0c;将鼠标移动到图标上会看到三个标记 可以使用下载&#xff0c;直接使用&#xff1a; 可以使用css文件使用&#xff1a; 首先点击购物车样式的选项&#xff0c;而后点击下图位置&#xff1a; 点击自己创…

成都农商银行2024春季校园招聘考试流程及及校招笔试需要重点考察什么?

成都农商银行校招笔试开摄像头吗&#xff1f; 提前模拟&#xff0c;检查好摄像头 成都农商银行校招笔试真题及题型&#xff1f; 成都农商银行校招使用的是智鼎测评题库&#xff0c;前尘无忧考试系统 34、目前&#xff0c;水和肥料都没有在农业中得到最佳使用&#xff0c;传统的…

【pycharm】调试模式中四个常用按钮介绍

【pycharm】调试模式中四个常用按钮介绍 在 PyCharm 的调试模式中&#xff0c;有四个常用的按钮&#xff0c;它们的功能如下&#xff1a; Step Over (F8)&#xff1a;单步执行&#xff0c;但在遇到函数调用时&#xff0c;不会进入函数内部&#xff0c;而是将整个函数作为一步执…