我是黑洞极客,欢迎来到算法竞赛系列!(先赞后看,养成习惯!)
二分查找
引言
对于查找一个数,我们在编程中会使用枚举,BUT,枚举效率太低了!这个时候就要运用另一个查找算法:二分查找。
也许你对 “二分查找” 很陌生,但你其实经常会用到:
当你猜数字时,会从中间猜,再根据提示压缩答案的范围;
当你查字典时,会先翻一页,看看你要找的词在这之前还是之后。
这都是二分的思想。
介绍
二分查找(Binary Search),又称折半查找,是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序储存结构,而且表中元素按关键字有序排列。该算法时间复杂度为O(logn)。
顾名思义,二分,就是不断减半,直到范围缩小到只有一个数,那个数就是答案。
代码框架
int find(int x, int *a, int n) // x为目标数,a为数组(下标从1开始),n为数组长度
{
int ans = -1; // 答案
int l = 1, r = n; // 左边界与右边界
while (l <= r)
{
int mid = (l + r) / 2; // 中间值
if (check(mid)) r = mid; // check()判断mid是否满足题意,是在左边
else l = mid + 1; // 不是在右边
}
ans = l; // 锁定答案
return ans;
}
这段代码挺长的,每次使用都要调用很麻烦,我懒得写为了简化代码,STL中有这两个函数:
upper_bound() & lower_bound()
upper_bound(begin, end, value)
lower_bound(begin, end, value)
begin 是数组的第一个元素的地址,end 是数组的最后一个元素的地址,value 是目标数值
upper_bound() 寻找的是数组中第一个大于目标值的数的地址
lower_bound() 寻找的是数组中第一个大于等于目标值的数的地址
而又因为返回的是地址,所以要减去begin。
这时,有人就要问了:那找最后一个小于和最后一个小于等于目标值的数怎么办?
很简单,第一个大于等于的就是最后一个小于等于的,减一后就是最后一个小于的。如果不理解的话,再多想几遍。
算法例题
查找
题目描述
输入 n 个不超过 10^9 的单调不减的(就是后面的数字不小于前面的数字)非负整数 ,然后进行 m 次询问。对于每次询问,给出一个整数 q,要求输出这个数字在序列中第一次出现的编号,如果没有找到的话输出 -1 。
输入格式
第一行 2 个整数 n 和 m,表示数字个数和询问次数。
第二行 n 个整数,表示这些待查询的数字。
第三行 m 个整数,表示询问这些数字的编号,从 1 开始编号。
输出格式
输出一行,m 个整数,以空格隔开,表示答案。
样例
输入
11 3
1 3 3 3 5 7 9 11 13 15 15
1 3 6输出
1 2 -1
解题思路
这是经典的二分查找题,与正常二分不同的是,目标值不一定在数组里。解决方法也很简单,如果在数组里,那必然:x == a[ans],判断一下就可以了。
题解
#include <bits/stdc++.h>
using namespace std;
int a[1000010];
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
while (m--)
{
int x;
scanf("%d", &x);
int ans = lower_bound(a, a+n, x) - a;
if (x == a[ans]) printf("%d ", ans+1);
else printf("-1 ");
}
return 0;
}
走到这里,我们成功GET到了二分查找,下次见,拜拜!