前言:
最近在学习算法,二分法是一个非常好的优化查找时间的方法,在列表有序的情况下,我们可以使用二分法加快查抄速率。虽然思路很简单,但是二分法很容易遇到边界问题,一不小心就会被边界卡住,做题的时候非常头疼。这里做一下小总结。PS:以下均为整数二分。
一:算法思路
二分法思路其实很简单,我们设置一个low指针指向有序数组的最低位置,high指针指向最高位置,然后将待查找的元素与(low+high)/2的位置比较。如果待查元素小于中间位置,那么就去右半边查找,否则就去左半边查找。(这是以为右半边元素肯定是大于这个中间元素的,以为数组元素有序,左半边亦然)
二:算法代码
2.1: 模板1
ps:完整代码会在最后面附上,这里只是代码片段。
while(low<high){
int mid = (low+high)/2;
if(q[mid]>=a[i]){
high = mid;
}else{
low = mid+1;
}
}
2.2: 模板2
while(low<high){
int mid = (low+high+1)/2;
if(q[mid]<=x){
low = mid;
}else{
high = mid-1;
}
}
关于上述代码的注意事项:
- 如果我们在修改的过程中,让low = mid那么一定要让low+high这里再加上一个1,则mid = (low+high+1)/2; 否则可能会导致死循环。这里可以自己推导以下,或者直接记住拉到。
- if(q[mid]>=a[i])这里的等于号,是我们可以自己修改的,有些题中这个等于号需要,有些题不需要。这也就变成了我们自己做题的时候需要注意的地方。下面讨论一下等于号加与不加的含义。
上面只是简单的给了俩个模板,下面分析一下这俩个模板的具体含义。包括等号的作用进行总结。
三: 算法演示
下这段代码是一段演示代码。就是定义了一个长度为12的数组,元素有序,但是有多个重复的5,来讨论一下等号的具体含义。
#include<iostream>
using namespace std;
int q[]={1,2,3,4,5,5,5,5,6,7,8,9,10};
int main(){
int low = 0;
int high = 9;
while(1){
float x;
cin>>x;
int low = 0;
int high = 12;
while(low<high){
int mid = (low+high)/2;
if(q[mid]>=x){
high = mid;
}else{
low = mid+1;
}
}
cout<<"low:"<<low<<"high:"<<high<<endl;
low = 0;
high = 12;
while(low<high){
int mid = (low+high+1)/2;
if(q[mid]<=x){
low = mid;
}else{
high = mid-1;
}
}
cout<<"low:"<<low<<"high:"<<high<<endl;
}
return 0;
}
3.1 加等号查找
我们将俩个模板都加上等号,查找5和5.5。运行看一下结果。(5为数组中存在的数,5.5不存在)
数组为:
int q[]={1,2,3,4,5,5,5,5,6,7,8,9,10};
-
对于查找到数组中存在的数来说:
可以看到,第一段代码查找的是左边界。且是闭区间。第二段代码查找到的是最右边界。也是闭区间。
-
对于查找数组中原本不存在的数来说:
可以看到,第一个模板查到的是最右边界,第二段代码查找的是最左边界。
3.2 不加等号查找
我们将俩个模板都去掉等号,查找5和5.5。运行看一下结果。(5为数组中存在的数,5.5不存在)
-
对于查找到数组中存在的数来说:
可以看到,第一段代码查找的是右边界。且是开区间。第二段代码查找到的是最左边界。也是开区间。
-
对于查找数组中原本不存在的数来说:
可以看到,第一个模板查到的是最右边界,第二段代码查找的是最左边界。
3.3 总结
可以看到,对于二分法而言,如果我们查找的是数组中本来就不存在的数字,那么加不加等号不影响结果。并且查找的结果永远都是,第一个模板是查找右开区间,第二个模板是左开区间。这里有一个自己的记忆方法:
如果是low = mid,那就是左开区间。
如果是high = mid,那就是右开区间。
(看最左边是low还是high)
但是如果我们要查找的数是数组中本来就有的,那么
加等号和不加等号中俩个模板的功能反过来了。首先可以确定的是,加等于号一定是尽可能的找到闭区间,而不加找到的是开区间。如果查找找的元素数组中原来就有且带等号,那么
low = mid找到的是右闭区间,high = mid是左开区间。
所以,在做二分的题目的时候,关键就是要取舍自己是否要加等号。加找到的是小于等于或者大于等于边界的位置,不加找到的一定是大于或者小于边界的位置。加等号后,俩个模板查找的功能就相反了。
low = mid变成右边界,high= mid是左边界。