浅说二分查找

引入

在一次商场宣传活动中,你被主持人邀请上台完一个“我想你猜”的游戏。游戏规则如下:主持人先想好一个数字,这个数字在1—1000之间,然后把数字写在纸条上,然后主持人叫你来猜这个数字是多少,每当你猜一个数字之后,如果正确那么就获胜,如果错误,主持人会告诉你你的答案大了还是小了,然后让你继续猜,最后根据你猜的次数来给你分配奖品。现请你设计一种策略保证每次猜数的次数尽可能少。
玩这个游戏,除非你会读心术或者内定获胜者,否则你只能凭空猜测,没有任何规律可言。但是我们就只能盲猜了吗?并不是,其实有一种可以快速逼近答案的方法——二分猜数。
方法为每次都猜当前区间[l,r]中间的那个数mid,如果主持人告诉你大了,那么答案就在区间[l,mid-1]之间,那么你下次就猜测[l,mid-1]中间那个数;如果主持人告诉你小了,那么答案就在[mid+1,r],那么你下次就猜测[mid+1,r]中间那个数,直到猜到为止。
那么这种猜数的方法在玩很多次游戏的情况,平均一局游戏猜多少次一定可以猜到这个数呢?这种策略的本质是一直把当前区间分成大致相等的两部分,所以我们思考对于当前区间最多被分多少次就会被分成长度为1的区间,并且答案又在这个区间内,那么答案就确定了。最开始区间[1,1000],长度1000,分一次之后长度为500,再分一次长度为250…实际上相当于对1000取一个log的答案,最后我们可以得到答案为10次左右。如果区间是[1,10亿]呢?我们会发现只需要30次左右我们就一定可以猜到答案,是不是猜测次数比随机盲猜好了太多?
其实生活中还有很多二分查找的例子,比如电路故障查找,求高次方程的零点,根据重量查找质量不合格的物品等等。对于一个长度为n的有序序列。如果依次查找,找到正确结果的时间复杂度为O(n),但是如果用二分的方法查找。每次排除一半的可能性,在剩下的一半里面找,这样每次查找,范围变为以前的一半。时间复杂度为O(log n)。注意,二分查找的使用前提是该序列是有序的,如果该序列无序,是不可以用二分查找的

二分查找STL

虽然二分的原理很简单,也很容易理解,但是我们在写代码的过程中,由于数据中查找的元素存在重复的数据,或者查找的元素不存在等等很多情况,所以对于不同的情况,二分的边界条件也有点区别,所以初学着在学习二分的过程中经常犯错而且找不到错误在那。所以这里我们学习一个二分的STL函数,使用函数就避免了很多手写的问题

lower_bound();

upper_bound();

lower_bound()

lower_bound(a+1,a+1+n,x);

二分查找有序表中第一个大于等于x的数的内存地址,如果需要转换成数组下标,后面需要加上“-a”,如果比末尾元素大,则返回数组最后一个元素的下一个位置。仅适用于非降序的有序表,如果是非升序的有序表,则需要重载:

lower_bound(a+1,a+1+n,x,greater<int>());

返回有序表中第一个小于等于x的数的位置,仅适用于非升序的有序表

upper_bound()

upper_bound(a+1,a+1+n,x);
;

二分查找有序表中第一个大于x的数的内存地址,如果需要转换成数组下标,后面需要加上“-a”,如果比末尾元素还大,则返回数组最后一个元素的下一个位置。仅适用于非降序的有序表,如果是非升序的有序表,则需要重载:

upper_bound(a+1,a+n+1,x,greater<int>());

返回有序表中第一个小于x的数的位置,仅适用于非升序的有序表

lower_bound和upper_bound使用的限制很大,只能用于有序不下降序列,但是在实际情况中,这个序列虽然是有序的,但是可能是下降的,也可能是一个结构体数组,我们需要根据结构体中某一个参数二分查找,这种情况,如果重新开一个数组来存储,就会多O(n)的时间复杂度,那么有没有办法对结构体数组二分查找呢?前面我们学习过STL中的sort和优先队列,对于结构体数组都需要重载小于运算符,二分查找STL也可以用相同的办法。

sort和优先队列的重载小于运算符的方法有一些区别,sort是重新写一个比较函数,优先队列是直接在结构体结点中重载小于运算符。二分查找STL的重载方式和优先队列的重载方式类似,都是直接在结构体结点内重载。

struct node {
	int num,id;
	friend bool operator < (node x,node y){
		return x.num<y.num;
	}
};
  • 16
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值