二分查找
二分查找是常见的算法,但是我发现我经常无法区分应当使用哪种变形来计算答案,因此这里做一下讲解.
标准写法
1 | ll l=___,r=___; |
判断条件的选择
判断条件可以理解为,每次要淘汰哪一部分,并通过l,r指针的移动来确定的
前提条件:假设现在我们有一个升序的数组
比如:我现在要查找第一个大于等于key的值
那么我们的条件就应该是每次循环都删去小于key的值,不能删去大于的,因为答案可能在这里面
于是我们的条件就写成:if(a[mid]<key) l=mid+1;
为什么是mid+1?因为l表示的是左边界,左边界移动了,就相当于淘汰了左边的小于key的值
同理,我们在写找第一个大于当前值时,只要改成<=即可
那么反过来呢?
没错,把条件和操作全反过来即可.现在我们要找第一个小于等于key的值
所以我们每次淘汰的是大于key的值
条件变为:if(a[mid]>key) r=mid-1;
答案的取值
弄清楚了怎么写条件,我们还需要弄清楚最后l还是r指向的才是答案.
我的方法是,假设第一次指向的就是正确答案,那么哪个指针指向就是正确答案.
所以也是就if对应的变化的指针,就是答案.
比如查找大于等于key时,if条件后面变化的是l,所以答案就是l所对应的值.
其他技巧
学习二分主要的目的不是为了二分查找,而是为了二分答案.判断条件有可能会更复杂.但是核心还是上面哪两点,首先知道我要删的是什么,其次答案会等于什么位置
最后,使用二分时不要忘记检查数据是否以及排序,答案是否一定满足升序关系,再加上适量的边界检查,二分就能拿下啦!
C++自带的二分
C++中,能使用lower_bound和upper_bound的有set,multi_set(多重集合),数组,vector
前者查找第一个大于等于key的值,后者找第一个大于key的值(先保证数组有序才行)
那么小于或小于等于呢?
小于可以先查大于等于的,再向前一步
小于等于可以先查大于的,再向前一步