主要使用场景场景
- 在有序序列中确定边界的行为
- 在处理许多问题的时候和前缀和一起出现
定义
二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。
时间复杂度
在使用诸如数组之类的顺序储存结构时,二分查找法的时间复杂度是对数级别的,O(log2n)
基本思想
查找的集合是有序的,这样可以通过利用顺序的比较来减少比较次数.
查找的范围X是查找次数n的指数级别,一般的,X=2^n.
利弊分析
利
- 比较次数少
- 好写
弊
- 要求顺序储存结构
- 要求有序的集合
语言相关
CPP
- 通过对stl的sort可以很方便的进行二分查找
涉及的函数主要有以下三个(需要头文件algorithm)
binary_search(起始地址,结束地址,要查找的数值)
返回的是是否存在这么一个数,是一个bool值。
lower_bound(起始地址,结束地址,要查找的数值)
lower_bound() 函数用于在指定区域内查找**不小于目标值(>=N)**的第一个元素。也就是说,使用该函数在指定范围内查找某个目标值时,最终查找到的不一定是和目标值相等的元素,还可能是比目标值大的元素。
重载格式
ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last,const T& val);
在 [first, last) 区域内查找不小于 val 的元素
ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last,const T& val, Compare comp);
在**[first, last)** 区域内查找第一个不符合 comp 规则的元素
此外,该函数还会返回一个正向迭代器,当查找成功时,迭代器指向找到的元素;反之,如果查找失败,迭代器的指向和 last 迭代器相同。
参数说明:
其中,first 和 last 都为正向迭代器,[first, last) 用于指定函数的作用范围;
val 用于指定目标元素;
comp 用于自定义比较规则,此参数可以接收一个包含 2 个形参(第二个形参值始终为 val)且返回值为 bool 类型的函数,可以是普通函数,也可以是函数对象。
返回值
函数lower_bound()在first和last中的前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置。如果所有元素都小于val,则返回last的位置。
可以使用这个函数的返回值-容器.begin()的方式来获取index
比如
vector<int>::iterator it=lower_bound(vec.begin(),vec.end(),20);
int index=it-vec.begin();
但需要注意
假设在一个0-19组成的大小是20的数组里查询19,那么这个index的值是19,查询20的时候,这个index的值是20(也就是实际上没有找到,这个值是数组的大小)
这里的实际范围[vec.begin(),vec.end())其中vec.end()就是20.可以通过下面这个代码检查是否找到:
if (it==vec.end())
{
printf("没找到\n");
}
这里也可以看出,vector.begin()实际上指向的是数组的下标是0的值,而vector.end()实际指向下标是N的值(在size是N的数组里不存在,会segmentation fault.)
upper_bound(起始地址,结束地址,要查找的数值)
upper_bound() 函数定义在头文件中,用于在指定范围内查找**大于目标值(>N)**的第一个元素。
重载格式
和上文几乎一致lower_bound()
实现
CPP
非
int l,r,mid;
l=0,r=X-1;
while (l<r)
{
mid=(l+r)>>1;
if (条件1)
{
r=mid;
}else {
l=mid+1;
}
}
注意
- 如果查找结果在范围内,那么最终的r一定是满足条件1的最靠近左侧的下标
- 如果查找的值在范围外,那么最后的r会停留在两端.(所以需要对是否在范围外做检测),常见的方法有1边界检测,2在左右两端加两个远大和远小的flag值.
- 大部分情况下最终结果的l和r是相等的,但是mid可以不等(这话不是废话?俺是nt)
例子
#define X 10
#define searched 20
vector<int> list(X);
for (int i = 0; i < X; i++)
{
list[i] = 2 * i;
}
//0 2 4 6 8 10 12 14 16 18
//0 1 2 3 4 5 6 7 8 9
int l,r,mid;
l=0,r=X-1;
while (l<r)
{
mid=(l+r)>>1;
if (list[mid]>=searched)
{
r=mid;
}else {
l=mid+1;
}
}
printf("%d %d %d\n",l,mid,r);
printf("%d %d %d\n",list[l],list[mid],list[r]);
注意
在上述查找过程中,
查询-1得到
0 0 0
0 0 0
查询0得到
0 0 0
0 0 0
查询1得到
1 0 1
2 0 2
查询5得到
3 3 3
6 6 6
查询9得到
5 5 5
10 10 10
查询10得到
5 5 5
10 10 10
查询18得到
9 8 9
18 16 18
查询200得到
9 8 9
18 16 18