当前位置:网站首页>模板系列-二分
模板系列-二分
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点上的答案。
边栏推荐
猜你喜欢
【系统设计与实现】基于flink的分心驾驶预测与数据分析系统
Use tencent cloud builds a personal blog
win10 system update error code 0x80244022 how to do
二叉树遍历之后序遍历(非递归、递归)入门详解
How to update Win11 sound card driver?Win11 sound card driver update method
Win11没有本地用户和组怎么解决
General syntax and usage instructions of SQL (picture and text)
Win10上帝模式干嘛的?Win10怎么开启上帝模式?
Open the door to electricity "Circuit" (3): Talk about different resistance and conductance
【我的电赛日记(二)】ADF4351锁相环模块
随机推荐
【我的电赛日记(二)】ADF4351锁相环模块
Mysql连接错误解决
In-depth understanding of Golang's Map
Mysql之MVCC
Mapreduce环境详细搭建和案例实现
Please make sure you have the correct access rights and the repository exists.问题解决
使用npx -p @storybook/cli sb init安装失败,手把手搭建专属的storybook
FP6195耐压60V电流降压3.3V5V模块供电方案
基于51单片机和物联网的智能家居系统(ESP8266物联网模块)
Win11电脑一段时间不操作就断网怎么解决
FP7195芯片PWM转模拟调光至0.1%低亮度时恒流一致性的控制原理
机器学习和深度学习中的梯度下降及其类型
win10任务栏不合并图标如何设置
HAL框架
arm ldr系列指令
A clean start Windows 7?How to load only the basic service start Windows 7 system
FP7195转模拟调光技术解决智能家居调光频闪和电感噪音的原理
STM32F1和F4的区别
LeetCode2 电话号码的字母组合
TCP三次握手、四次挥手