【算法笔记】 二分查找与二分答案入门超详细版,一篇给你讲懂
算法笔记 —— 二分入门
二分其实真挺重要的,因为他的应用范围比较广。竞赛中,许多知识也是由二分变出来的;甚至生活中,你也可以用二分解决一些看似比较难的问题,惊呆你的小伙伴😮😮😮
引入(大佬请直接跳到下一个环节)
猜数你们应该都玩过吧,你猜一个数,我会回答“大了”,“小了”或”猜出来了“。假设猜数的范围为1~100,我们需要尽可能地用最小的次数来猜出那个数,请问我们需要几次?
这个问题十分好想,每次我们都需要尽量缩小范围。如果你第一次猜20,30这些数,那万一那个数是100呢?就会导致下次猜数的范围偏大,所以有没有哪个数是可以固定住下一次猜数时的范围呢?有的有的。我们第一次直接取中间的50,这时不管那个要被猜的数在左在右,我们都可以缩小一半的范围。接下来继续找中间数维持平衡,直到最后一个值。
看一下需要猜几次,每次取数会缩小一半的范围,那就是 \(\frac{100}{2^x}\) , \(2^7 = 128 > 100\) ,所以1~100范围内的只需猜7次。
这其实就是二分答案的一种基础题。
二分查找&二分答案
二分查找
概念:有序数组内找需求的已知的数的位置
假设这个数组是一段木头:
若没找到该数且当前数大了,把大于等于当前数的这半儿给砍了,判断没砍掉的;
若没找到该数且当前数小了,把小于等于当前数的这半儿给砍了,判断没砍掉的;
若找到该数,输出题目木要求需要输出的。
上面的这种思路不是唯一的,真实思路需根据实际题目调整
设被查找的有序数组={1,2,3,……100}(1到100,100个数),查找数为20
如图:

(不经意间展示画画水平)
给出代码
向左找,比如说问你某个值(那个值可以在一个数组中出现多次),在这个有序数组最先出现的位置
while (l<=r){
int mid=(l+r)/2;
if (a[mid]==x) {cout <<mid; return 0;}
else if (a[mid]>x) r=mid-1;
else l=mid+1;
}
向右找,比如说问你某个值(那个值可以在一个数组中出现多次),在这个有序数组最后出现的位置
while (l<=r){
int mid=(l+r)/2;
if (a[mid]==x) cout <<mid;
else if (a[mid]>x) l=mid+1;
else r=mid-1;
}
课后题目
二分答案
概念:在一段具有单调性的区间内,通过一堆判断条件找到满足条件的未知解。
首先我们重点聊聊这个单调性是什么。具有单调性的区间指的不一定是这个区间内的所有数有序,他指的是这段区间他的性质有序。就是说,这段区间内,若某一个值满足题目的判断条件,那么这个值左边的一大堆就也满足条件(看不懂没关系,直接看后面)
为什么必须要有单调性呢?如果这段区间不满足单调性,你判断到某个值不满足单调性,你有怎么可以说前面的所有值都不满足判断条件呢?
形象点,还是把这个区间看成一段木头。如果这段木头一节粗一节细,你判断到一节木头太细了(不满足条件),你能说他前面的也太细了(也不满足条件)吗,只有你先让这段木头从细到粗地排列(先把数组排序),你才可以判断到一节太细,把前面的全排了(前面全是比他更细的);一节太粗,把后面的全排了(后面全是比他更粗的)。
其实二分答案就相当于猜答案满不满足条件。用代码表示,就是先定 \(l,r\) ( \(l\) 表示当前下限, \(r\) 表示当前上限),取中间值,满足处理一遍,不满足又处理一遍(结合上面猜数字的引入来理解)。
给出代码:
向左找,比如说问最先满足条件的值是什么
while (l<=r){
int mid=(l+r)/2;
if (chk(mid)) r=mid-1,ans=mid;
else l=mid+1;
}
向右找,比如说问最后满足条件的值是什么
while (l<=r){
int mid=(l+r)/2;
if (chk(mid)) r=mid-1,ans=mid;
else l=mid+1;
}
课后题目
一些错误
有些人在取中间值时会出错,这是为什么?
因为 \(mid\) 溢出了,这时我们只需要把 \(mid=(l+r)/2\) 写成 \(mid=l+(r-l)/2\) 就行了。
\(while(l<=r)\) 与 \(while(l<r)\) 有何区别?
\(while(l<=r)\) 是在检查所有数,判断目标是否存在。而 \(while(l<r)\) 则是在找最优解或边界。
如果你二分出错,不妨两者之间换一下试试。
课后练习
相信屏幕前聪明的你一定会了吧,来做做这个题单吧!

浙公网安备 33010602011771号