模板系列-二分

本文详细介绍了二分搜索算法,包括整数和实数上的应用,以及在有序序列中查找元素的步骤。非递归和递归两种实现方式被提出,并分析了它们的时间复杂度和空间复杂度。在实际使用中,注意搜索范围的设定、避免溢出以及防止丢失可能的答案。二分搜索算法在处理有序数据时展现出高效性能,时间复杂度为O(logn)。
摘要由CSDN通过智能技术生成

二分模板

1、整数上的二分搜索

整数上的二分搜索,因为缩小搜索范围时,有可能 r = m i d − 1 r=mid-1 r=mid1 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 rl>eps 作为循环条件, e p s eps eps 为一个较小的数,如 1 e − 7 1e-7 1e7 等。为避免丢失可能解,缩小范围时 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} 1030 精度,一般情况都可以解决问题。

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 lowhigh 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=middle1 ,去前半部分搜索。

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、二分搜索需要注意的几个问题

  1. 必须满足有序性。
  2. 搜索范围。初始时,需要指定搜索范围,如果不知道具体范围,正数可以采用范围 [ 0 , i n f ] [0,inf] [0,inf] ,有可能为负数可以采用范围[-inf,inf],inf为无穷大,通常设定为 0 x 3 f 3 f 3 f 3 f 0x3f3f3f3f 0x3f3f3f3f
  3. 二分搜索。一般情况下, 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+(rl)/2 。判断二分搜索结束的条件,以及当判断 m i d mid mid 可行时到前半部分搜索,还是到后半部分搜索,需要具体问题具体分析。
  4. 答案是什么。特别小心搜索范围减少时,是否丢失在mid点上的答案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值