二分简述

算法思想来源:Acwing

简介:如果我们有一个性质,能将一个区间划分成两部分,二分能将这个性质的边界求出来。

如图,两个箭头指向的两个点就是二分的两个边界点。

性质: 能二分的区间不一定具有单调性,但是具有单调性的区间一定能二分。

过程:例如我们要从一组从大到小排列的一组数中找出数x,那么我们先让这个数与区间的中间点的值作比较,如果中间值大于x,那么我们就去中间点的值左边的区间找,继续选中间点的值与x比较继续找,反之,如果中间点的值小于x,那么我们就去中间点的值的右边去找,继续选中间点的值与x比较继续找。就这样一直分下去,直到找到想要的值。

举个实例:比如我们要从1-100中找出一个数字,例如80,按照二分查找的方法,先查100/2 = 50,50比80小,那么再查(100+50)/2=75,还是比80小,再查(100+75)/2=87,比80大,接下来再取75-87的中间数与80进行比较,最后查到80。

模板:

二分的话有两个模板,一个是求上图红色区间的边界点的,一个是求上图绿色区间的边界点的,两个模板是不一样的。

int bsearch1(int l,int r)
{
	while(l<r)
	{
		int mid=l+r+1>>1; 
		if(check(mid)) l=mid;//如果满足红色区间的条件,那就去右边找边界点,让l=mid,r依旧是r,因为mid这个点满足条件,所以也算在右边区间范围内 
	    else r=mid-1;//如果不满足红色区间的条件,那就去左边找边界点,让r=mid-1,l依旧是l,因为mid这个点不满足条件,所以不算在右边区间范围内 
	}
}
int bsearch2(int l,int r)
{
	while(l<r)
	{
		int mid=l+r>>1; 
		if(check(mid)) r=mid;//如果满足绿色区间的条件,那就去左边找边界点,让r=mid,l依旧是l,因为mid这个点满足条件,所以也算在左边区间范围内
		else l=mid+1;//如果不满足绿色区间的条件,那就去右边找边界点,让l=mid+1,r依旧是r,因为mid这个点不满足条件,所以不算在右边区间范围内
	}
 } 

 我们可以发现第一个模板与第二个模板有些不同,主要体现在求中值点的时候,为什么第一个模板求中值点时l+r除以2的时候还要提前加1呢?

因为l+r除以2是向下取整的,如果l正好等于r-1,如果不加1,mid就会等于l,程序就会在if(check(mid)) l=mid;这条语句中死循环下去。

例题:

例题来源:Acwing 

#include <iostream>
#include <algorithm>

using namespace std;

const int maxn=1e5+7;

int n,q,x;
int a[maxn];

int main()
{
	cin>>n>>q;
	for(int i=0;i<n;i++) scanf("%d",&a[i]);
	while(q--)
	{
		scanf("%d",&x);
		int l=0,r=n-1;
		while(l<r)
		{
			int mid=(l+r)>>1;	
			if(a[mid]>=x) r=mid;//找到大于等于x和小于x的边界值,即所求元素的起始位置 
			else l=mid+1;
		}
		if(a[l]!=x) printf("-1 ");
		else printf("%d ",l);
		l=0,r=n-1;
		while(l<r) 
		{
			int mid=l+r+1>>1;	
			if(a[mid]<=x) l=mid;//找到小于等于x和大于x的边界值,即所求元素的终止位置 
			else r=mid-1;
		}
		if(a[l]!=x) printf("-1 ");
		else printf("%d",l);
		puts(""); 
	}
	
	return 0;
 } 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值