题目描述
给定一个按照升序排列的长度为 n n n 的整数数组,以及 q q q 个查询。
对于每个查询,返回一个元素 k k k 的起始位置和终止位置(位置从 0 0 0 开始计数)。
如果数组中不存在该元素,则返回 -1 -1
。
输入格式
第一行包含整数 n n n 和 q q q,表示数组长度和询问个数。
第二行包含 n n n 个整数(均在 1 ∼ 1 0 9 1\ \sim\ 10^{9} 1 ∼ 109 范围内),表示完整数组。
接下来 q q q 行,每行包含一个整数 k k k,表示一个询问元素。
输出格式
共 q q q 行,每行包含两个整数,表示所求元素的起始位置和终止位置。
如果数组中不存在该元素,则返回 -1 -1
。
数据范围
1
≤
n
≤
100000
1 ≤ n ≤ 100000
1≤n≤100000
1
≤
q
≤
10000
1 ≤ q ≤ 10000
1≤q≤10000
1
≤
k
≤
10000
1 ≤ k ≤ 10000
1≤k≤10000
输入样例:
6 3
1 2 2 3 3 4
3
4
5
输出样例:
3 4
5 5
-1 -1
算法
手写的二分查找
二分是一种很精妙而又容易写错的算法。
二分的基本思想
假设有一个数列 q q q,在该数列中存在一个区间,该区间左端元素的序号为 l l l,右端元素的序号为 r r r。
存在一个临界点
p
p
p(边界),使得其右边的所有元素(包含
p
p
p)都满足性质
A
A
A,而其他元素都不满足性质
A
A
A。
这就是二分的一个基本要求:二分性
时间复杂度
O
(
l
o
g
n
)
O(logn)
O(logn)
因为每次可以把查找范围缩小一半,所以设总共查找的次数为
x
x
x次,则
2
x
≥
n
2^{x}\ \ge\ n
2x ≥ n,所以查找次数为
c
e
i
l
(
log
2
n
)
ceil(\log_{2}n)
ceil(log2n),故时间复杂度为
O
(
l
o
g
n
)
O(logn)
O(logn)
C++ 代码
#include <iostream>
using namespace std;
const int N = 100010;
int n, m;
int q[N];
int main()
{
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i ++ ) scanf("%d", &q[i]);
while (m -- )
{
int x;
scanf("%d", &x);
int l = 0, r = n - 1;
while (l < r)
{
int mid = l + r >> 1;
if (q[mid] >= x) r = mid;
else l = mid + 1;
}
if (q[l] != x) cout << "-1 -1" << endl;
else
{
cout << l << ' ';
int l = 0, r = n - 1;
while (l < r)
{
int mid = l + r + 1 >> 1;
if (q[mid] <= x) l = mid;
else r = mid - 1;
}
cout << l << endl;
}
}
return 0;
}
解法2,STL
STL里提供了lower_bound()和upper_bound这两个函数
所以得出
#include<iostream>
#include<algorithm>
using namespace std;
int n,q,k,a[100010];
int main()
{
cin >> n >> q;
for(int i = 0;i < n;i++) cin >> a[i];
while(q -- )
{
cin >> k;
if(binary_search(a, a + n, k))
printf("%d %d\n", lower_bound(a, a + n, k) - a, upper_bound(a, a + n, k) - a - 1);
else puts("-1 -1");
}
return 0;
}