当前位置:网站首页>模板系列-二分
模板系列-二分
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点上的答案。
边栏推荐
- How to reinstall Win7 system with U disk?How to reinstall win7 using u disk?
- 将SSE指令转换为ARM NEON指令
- 蓝牙温度检测系统(基于BT08-B蓝牙模块)
- Daily - Notes
- DP4056电源保护芯片锂电池pin对pinTP4056
- DP4301无线收发SUB-1G芯片兼容CC1101智能家居
- 二叉树遍历之后序遍历(非递归、递归)入门详解
- Win11 computer off for a period of time without operating network how to solve
- MATLAB制作简易小动画入门详解
- vscode镜像
猜你喜欢
随机推荐
Actual combat Meituan Nuxt +Vue family bucket, server-side rendering, mailbox verification, passport authentication service, map API reference, mongodb, redis and other technical points
Failed to install using npx -p @storybook/cli sb init, build a dedicated storybook by hand
flink+sklearn——使用jpmml实现flink上的机器学习模型部署
ARMv8虚拟化
FP7128内置MOS降压恒流调光深度0.01%高辉共阳调光方案
STM32F1和F4的区别
刷卡芯片CI520可直接PIN对PIN替换CV520支持SPI通讯接口
Win11怎么在右键菜单添加一键关机选项
【我的电赛日记(二)】ADF4351锁相环模块
Binder机制(下篇)
jest test, component test
Win10 Settings screen out from lack of sleep?Win10 set the method that never sleep
What should I do if Windows 10 cannot connect to the printer?Solutions for not using the printer
Lightweight AlphaPose
Configure clangd for vscode
General code for pytorch model to libtorch and onnx format
FP7195大功率零压差全程无频闪调光DC-DC恒流芯片(兼容调光器:PWM调光,无极调光,0/1-10V调光)
FP6293电池升压5V-12V大电流2APWM模式升压方案
win10怎么设置不睡眠熄屏?win10设置永不睡眠的方法
小T成长记-网络篇-1-什么是网络?









