基础算法:一次性理解二分算法

server/2024/12/22 20:59:52/

二分查找

  • 1.理解
  • 2.三种模板
    • 2.朴素模板
      • 注意事项
    • 3.左端点模板
      • 注意事项
    • 4.右端点模板
      • 注意事项
  • 3.典型例题
    • 3.1题目要求
    • 3.2题目解析
    • 3.3实现思想
      • 寻找左边界思路
      • 寻找右边界思路:
    • 3.4代码实现
    • 3.5注意事项

1.理解

本质:二段性

首先我们需要打破我们的固有思维:二分查找不是只能使用在有序数组上!!!它使用的最本质的因素是因为具有二段性。

不只是只有有序才能使用二分,而是只要有二段性就能使用二分。

那么什么是二段性呢?

二段性可以理解成将一段区间划分成为两部分,这两部分都有一个各自的特点。

2.三种模板

2.朴素模板

该模板最常用于找一个特有的位置,即只有一个满足条件的情景。

int left, right; //定义左右指针
while(left <= right)
{int mid = left + (right - left) / 2; //防止溢出if(...) right = mid - 1;else if(...) left = mid + 1;else return mid;
}

注意事项

(1)循环条件

left <=  right ,因为left = right的时候也需要判断该点符不符合题意要求。

(2)防止溢出

如果直接(left + right)/ 2的话,那么当left和right过大时就会超出int存储的范围,因此使用减法,来确保没有溢出。right - left为所要查找区间的长度,再除以2就是区间长度的一半,这样就可以找到中间位置。

3.左端点模板

通常用于所查找区间内多个值都符合要求时,该区间的最左边端点。常见于一部分区间小于,一部分区间大于等于这种情形。

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

注意事项

1.为什么right = mid 而不是mid - 1

因为mid处的值也可能符合题目要求

​2.求mid的时候,没有+1(此处可以结合下面例题理解,例题有详细解释)

此做法是为了防止死循环。如果区间只剩2个值,如果不加一就是取的左边的点,这样left就会移动,如果+1之后,right = mid就会一直是右边的点,那么right就不会移动,造成死循环。

在这里插入图片描述
​ 3.对于left = right的情况,再根据题意来判断从而特殊处理。

4.右端点模板

通常用于所查找区间内多个值都符合要求时,该区间的最右边端点。

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

注意事项

​1.为什么left = mid 而不是mid +1

因为mid处的值也可能符合题目要求

​2.求mid的时候,有+1(此处可以结合下面例题理解)

为了防止死循环。如果区间只剩2个值,如果不加一就是取的左边的点,而左端点处left可能会一直不移动,这样就会造成死循环。

在这里插入图片描述

​3.对于left = right的情况,再根据题意来判断从而特殊处理。

3.典型例题

3.1题目要求

在这里插入图片描述

3.2题目解析

由于题目说明为非递减数组,且使用O(logN)的时间复杂度解决,因此使用二分查找。​查找满足条件的元素的第一个和最后一个位置,本质就是找到其左端点和右端点,直接套入模板即可。

3.3实现思想

方便叙述,使用x 表⽰该元素, resLeft 表示左边界, resRight 表示右边界。

寻找左边界思路

我们注意到以左边界划分的两个区间的特点:
(1)左边区间 [left, resLeft - 1] 都是小于x的。
(2)右边区间(包括左边界) [resLeft, right] 都是大于等于 x 的;

因此,关于 mid 的落点,我们可以分为两种情况。

情况一:
当我们的 mid 落在[left, resLeft-1]区间的时候,也就是 nums[mid] < target。说明[left, mid]都是可以舍去的,此时更新 left 到 mid + 1 的位置,
情况二:
继续在 [mid + 1, right] 上寻找左边界;当 mid 落在 [resLeft, right] 的区间的时候,也就是 nums[mid] >= target。说明 [mid + 1, right](因为 mid 可能是最终结果,不能舍去)是可以舍去的,此时更新 right 到 mid 的位置,继续在 [left, mid] 上寻找左边界;由此,就可以通过⼆分,来快速寻找左边界;

注意:这⾥找中间元素需要向下取整。(即求中点时是否+1)
因为后续移动左右指针的时候:
左指针: left = mid + 1,是会向后移动的,因此区间是会缩小的;
右指针: right = mid ,可能会原地踏步(⽐如:如果向上取整的话,如果剩下1,2 两个元素, left =1,right = 2,mid = 2。更新区间之后,left,right,mid 的值没有改变,就会陷⼊死循环)。
因此⼀定要注意,当 right = mid 的时候,要向下取整。

寻找右边界思路:

我们注意到右边界的特点:
左边区间 (包括右边界) [left, resRight] 都是小于等于x的;
右边区间 [resRight+ 1, right] 都是大于x 的;
因此,关于mid 的落点,我们可以分为下⾯两种情况:
情况一:
当我们的 mid 落在 [left, resRight] 区间的时候,说明 [left, mid - 1]( mid 不可以舍去,因为有可能是最终结果) 都是可以舍去的,此时更新 left 到 mid的位置。
情况二:
当 mid 落在 [resRight+ 1, right] 的区间的时候,说明 [mid, right] 内的元素
是可以舍去的,此时更新 right 到 mid - 1 的位置;由此,就可以通过⼆分,来快速寻找右边界;
注意:这⾥找中间元素需要向上取整。
因为后续移动左右指针的时候:
左指针: left = mid ,可能会原地踏步(⽐如:如果向下取整的话,如果剩下1,2 两个元素, left = 1, right = 2,mid = 1 。更新区间之后, left,right,mid 的值没有改变,就会陷⼊死循环)。
右指针: right = mid - 1 ,是会向前移动的,因此区间是会缩⼩的;
因此⼀定要注意,当 right = mid 的时候,要向下取整。

3.4代码实现

  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)left = mid + 1;else //nums[mid] > targetright = mid;}if(nums[right] != target)return { -1, -1};int begin = left;right = nums.size() - 1;while(left < right){int mid = left + (right - left + 1) / 2;if(nums[mid] <= target)left = mid;elseright = mid - 1;}return {begin, right};}

3.5注意事项

1.特殊情况如果为空数组,则会发生越界--->特殊处理
2.如果压根没有这个元素,那么就无法找到左端点,则直接判断,如果没找到,返回即可。

http://www.ppmy.cn/server/3782.html

相关文章

Blender生成COLMAP数据集

最近在做3DGS方向&#xff0c;整理了一下Blender生成自己的数据集。 1 Introduction 在Blender中构建场景&#xff08;light, object, camera&#xff09;&#xff0c;利用Blender的python脚本对其渲染&#xff0c;导出多视角下渲染出的RGB图和depth map&#xff0c;并将trans…

美国硅谷站群服务器如何提高网站性能

美国硅谷站群服务器可以通过多种方式提高网站性能&#xff0c;那么美国硅谷站群服务器如何提高网站性能。Rak部落小编为您整理发布美国硅谷站群服务器如何提高网站性能。 美国硅谷站群服务器提高网站性能主要包括以下几点&#xff1a; 1. 使用内容分发网络(CDN)&#xff1a;通过…

从零开始写一个RTSP服务器(二)RTSP协议的实现

目录 写在前面一、创建套接字二、解析请求三、OPTIONS响应四、DESCRIBE响应五、SETUP响应六、PLAY响应七、源码八、测试 写在前面 此系列只追求精简&#xff0c;旨在学习RTSP协议的实现过程&#xff0c;不追求复杂完美&#xff0c;所以这里要实现的RTSP服务器为了简单&#xf…

unity socket udp 连接

使用此方法有助于udp在局域网内稳定的连接运行&#xff0c;已经过验证&#xff0c;为了保持彻底的稳定&#xff0c;可以考虑加入ping-pang进行网络处理&#xff0c;如果为了安全&#xff0c;请使用加密TCP 如果您要在大规&#xff0c;大项目的游戏中使用网络技术&#xff0c;建…

YTM32使用eTMR定时器产生1Hz低频率PWM信号

YTM32使用eTMR定时器产生1Hz低频率PWM信号 文章目录 YTM32使用eTMR定时器产生1Hz低频率PWM信号需求软件模拟PWM需求分析 - why not&#xff1f;解题思路总结 需求 客户使用YTM32B1LE05微控制器&#xff08;下文简称LE05&#xff09;开发车载ECU&#xff0c;本机作为传感器设备…

Linux 操作系统非缓冲区的文件操作、时间编程

1、文件操作 1.1 基于缓冲区的文件操作 基于缓冲区的文件操作---高级Io 以f开头的是基于缓冲区的文件操作 printf是一个基于缓冲区的函数 输出条件&#xff1a; 1.程序正常运行 2.遇到换行\n也能输出 3.缓存区内存已满 1024大小 4.遇到fflush&#xff08;stdout&a…

K8s: 运行Pod时的root用户和非root用户的安全相关配置

关于 root 用户 1 &#xff09;概述 docker 容器运行起来&#xff0c;默认是 root 用户这样运行起来后&#xff0c;基本不会遇到权限相关问题带来的问题是: 权限过大&#xff0c;被攻击后会遇到严峻挑战基于这个问题&#xff0c;K8s提出了特权用户的概念在容器启动时&#xff…

iOS最新外部符号加载

介绍 iOS外部符号加载方式有两种&#xff1a;懒加载和非懒加载。 懒加载&#xff1a;首次调用该符号才加载。 非懒加载&#xff1a;app启动时加载。 非懒加载 默认加载方式。比如下面的代码&#xff0c;外部符号调用会先替换成一个桩函数&#xff0c;在__TEXT,__stubs段会…