当前位置:网站首页>模板系列-二分
模板系列-二分
2022-08-02 14:10:00 【老顽固也可爱】
二分模板
1、整数上的二分搜索
整数上的二分搜索,因为缩小搜索范围时,有可能 r = m i d − 1 r=mid-1 r=mid−1或 l = m i d + 1 l=mid+1 l=mid+1,因此可以用 a n s ans ans 记录可行解。是否需要减 1 或加 1 ,要根据具体问题分析。
l=a;
r=b; //初始搜索范围
while(l<=r) {
int mid=(l+r)/2;
if(judge(mid)) {
ans=mid; //记录可行解
r=mid-1;
}
else
l=mid+1;
}
2、实数上的二分搜索
实数上的二分搜索不可以直接比较大小,可以采用 r − l > e p s r-l>eps r−l>eps 作为循环条件, e p s eps eps 为一个较小的数,如 1 e − 7 1e-7 1e−7 等。为避免丢失可能解,缩小范围时 r = m i d r=mid r=mid 或 l = m i d l=mid l=mid ,循环结束时返回最后一个可行解。
l=a; r=b; //初始搜索范围
while(r-l>eps){
//判断差值
double mid=(l+r)/2;
if(judge(mid))
l=mid; //l记录了可行解,循环结束后返回答案l
else
r=mid;
}
// l 是答案
还可以运行固定的次数,如运行100次,可达 1 0 − 30 10^{-30} 10−30 精度,一般情况都可以解决问题。
l=a;
r=b;
for(int i=0; i<100; i++) {
//运行100次
double mid=(l+r)/2;
if(judge(mid))
l=mid;
else
r=mid;
}
// l 是答案
3、二分搜索详解
例如,给定n个元素序列,这些元素是有序的(假定为升序),从序列中查找元素x。
用一维数组S[]存储该有序序列,设变量low和high表示查找范围的下界和上界,middle表示查找范围的中间位置,x为特定的查找元素。
3.1 算法步骤
(1)初始化。令low=0,即指向有序数组S[]的第一个元素;high=n−1,即指向有序数组S[]的最后一个元素。
(2)判定low≤high是否成立,如果成立,转向第3步,否则,算法结束。
(3)middle=(low+high)/2,即指向查找范围的中间元素。如果数量较大,为避免low+high溢出,可以采用middle=low+(high-low)/2。
(4)判断x与S[middle]的关系。如果x=S[middle],则搜索成功,算法结束;如果x>S[middle],则令low=middle+1;否则令high=middle−1,转向第2步。
3.2 算法实现
用 BinarySearch(int n, int s[], int x)
函数实现折半查找算法,其中 n n n 为元素个数, s [ ] s[] s[] 为有序数组, x x x 为待查找元素。 l o w low low 指向数组的第一个元素, h i g h high high 指向数组的最后一个元素。如果 l o w ≤ h i g h low≤high low≤high, m i d d l e = ( l o w + h i g h ) / 2 middle=(low+high)/2 middle=(low+high)/2,即指向查找范围的中间元素。如果 x = S [ m i d d l e ] x=S[middle] x=S[middle] ,搜索成功,算法结束;如果 x > S [ m i d d l e ] x>S[middle] x>S[middle],则令 l o w = m i d d l e + 1 low=middle+1 low=middle+1 ,去后半部分搜索;否则令 h i g h = m i d d l e − 1 high=middle−1 high=middle−1 ,去前半部分搜索。
3.2.1 非递归算法
int BinarySearch(int s[],int n,int x) {
//二分查找非递归算法
int low=0,high=n-1; //low指向数组的第一个元素,high指向数组的最后一个元素
while(low<=high) {
int middle=(low+high)/2; //middle为查找范围的中间值
if(x==s[middle]) //x等于查找范围的中间值,算法结束
return middle;
else if(x>s[middle]) //x大于查找范围的中间元素,则从左半部分查找
low=middle+1;
else //x小于查找范围的中间元素,则从右半部分查找
high=middle-1;
}
return -1;
}
3.2.2 递归算法
递归有自调用问题,增加两个参数 low 和 high来标记搜索范围的开始和结束。
int recursionBS(int s[],int x,int low,int high) {
//二分查找递归算法
//low指向搜索区间的第一个元素,high指向搜索区间的最后一个元素
if(low>high) //递归结束条件
return -1;
int middle=(low+high)/2; //计算middle值(查找范围的中间值)
if(x==s[middle]) //x等于s[middle],查找成功,算法结束
return middle;
else if(x<s[middle]) //x小于s[middle],则从前半部分查找
return recursionBS(s,x,low,middle-1);
else //x大于s[middle],则从后半部分查找
return recursionBS(s,x,middle+1,high);
}
3.2.3 算法分析
3.2.3.1 时间复杂度
二分查找算法,时间复杂度怎么计算呢?如果用T(n)来表示n个有序元素的二分查找算法的时间复杂度,那么:
- 当n=1时,需要一次比较,T(n)=O(1)。
- 当n>1时,待查找元素和中间位置元素比较,需要O(1)时间,如果比较不成功,那么需要在前半部分或后半部分搜索,问题的规模缩小了一半,时间复杂度变为 T ( n 2 ) T(\frac{n}{2}) T(2n) 。
二分查找的非递归算法和递归算法查找的方法是一样的,时间复杂度相同,均为 O ( l o g n ) O(logn) O(logn)。
3.2.3.2 空间复杂度
二分查找的非递归算法中,变量占用了一些辅助空间,这些辅助空间都是常数阶的,因此空间复杂度为 O ( 1 ) O(1) O(1)。
二分查找的递归算法,除了使用一些变量外,递归调用还需要使用栈来实现。在递归算法中,每一次递归调用都需要一个栈空间存储,那么我们只需要看看有多少次调用。假设原问题的规模为n,那么第一次递归就分为两个规模为 n 2 \frac{n}{2} 2n 的子问题,这两个子问题并不是每个都执行,只会执行其中之一。因为和中间值比较后,要么去前半部分查找,要么去后半部分查找;然后再把规模为 n 2 \frac{n}{2} 2n 的子问题继续划分为两个规模为 n 4 \frac{n}{4} 4n 的子问题,选择其一;继续分治下去,最坏的情况会分治到只剩下一个数值,那么算法执行的节点数就是从树根到叶子所经过的节点,每一层执行一个,直到最后一层,如图所示。
递归调用最终的规模为1,即 n 2 x = 1 \frac{n}{2x}=1 2xn=1,则 x = l o g n x=logn x=logn 。假设阴影部分是搜索经过的路径,一共经过了 l o g n logn logn 个节点,也就是说递归调用了 l o g n logn logn 次。递归算法使用的栈空间为递归树的深度,因此二分查找递归算法的空间复杂度为 O ( l o g n ) O(logn) O(logn) 。
4、二分搜索需要注意的几个问题
- 必须满足有序性。
- 搜索范围。初始时,需要指定搜索范围,如果不知道具体范围,正数可以采用范围 [ 0 , i n f ] [0,inf] [0,inf] ,有可能为负数可以采用范围[-inf,inf],inf为无穷大,通常设定为 0 x 3 f 3 f 3 f 3 f 0x3f3f3f3f 0x3f3f3f3f。
- 二分搜索。一般情况下, m i d = ( l + r ) / 2 mid=(l+r)/2 mid=(l+r)/2 或 m i d = ( l + r ) > > 1 mid=(l+r)>>1 mid=(l+r)>>1 。如果l和r特别大,为避免 l + r l+r l+r 溢出,可以采用 m i d = l + ( r − l ) / 2 mid=l+(r-l)/2 mid=l+(r−l)/2 。判断二分搜索结束的条件,以及当判断 m i d mid mid 可行时到前半部分搜索,还是到后半部分搜索,需要具体问题具体分析。
- 答案是什么。特别小心搜索范围减少时,是否丢失在mid点上的答案。
边栏推荐
- Summarize computer network super comprehensive test questions
- Yolov5 official code reading - prior to transmission
- 【我的电赛日记(完结)---2021全国大学生电子设计竞赛全国一等奖】A题:信号失真度测量装置
- 【系统设计与实现】基于flink的分心驾驶预测与数据分析系统
- CS4398音频解码替代芯片DP4398完全兼容DAC解码
- DP4301无线收发SUB-1G芯片兼容CC1101智能家居
- FP7195大功率零压差全程无频闪调光DC-DC恒流芯片(兼容调光器:PWM调光,无极调光,0/1-10V调光)
- In-depth understanding of Golang's Map
- 日常-笔记
- win10 system update error code 0x80244022 how to do
猜你喜欢
随机推荐
DP1332E刷卡芯片支持NFC内置mcu智能楼宇/终端poss机/智能门锁
2021-10-14
Win10 cannot directly use photo viewer to open the picture
Win7怎么干净启动?如何只加载基本服务启动Win7系统
Win11 computer off for a period of time without operating network how to solve
使用libcurl将Opencv Mat的图像上传到文件服务器,基于post请求和ftp协议两种方法
Win11 keeps popping up User Account Control how to fix it
Win11怎么在右键菜单添加一键关机选项
KiCad常用快捷键
CI24R1小模块2.4G收发模块无线通信低成本兼容si24r1/XN297超低功耗
蓝牙温度检测系统(基于BT08-B蓝牙模块)
jest测试,组件测试
IPV4和IPV6是什么?
Win7 encounters an error and cannot boot into the desktop normally, how to solve it?
Win11 system cannot find dll file how to fix
cmake配置libtorch报错Failed to compute shorthash for libnvrtc.so
Letter combination of LeetCode2 phone number
flink+sklearn——使用jpmml实现flink上的机器学习模型部署
TCP三次握手、四次挥手
推开机电的大门《电路》(三):说说不一样的电阻与电导