题目描述:
统计一个数字在排序数组中出现的次数。
输入:
每个测试案例包括两行:
第一行有1个整数n,表示数组的大小。1<=n <= 10^6。
第二行有n个整数,表示数组元素,每个元素均为int。
第三行有1个整数m,表示接下来有m次查询。1<=m<=10^3。
下面有m行,每行有一个整数k,表示要查询的数。
输出:
对应每个测试案例,有m行输出,每行1整数,表示数组中该数字出现的次数。
样例输入:
8 1 2 3 3 3 3 4 5 1 3
样例输出:
4
第一眼做法:从头扫到尾。统计各个数据出现的次数,时间复杂度为O(n*m),显然会TLE;
正解:二分查找。如果直接使用二分查找去做的话,最坏的情况查找的时间复杂度仍然为O(n),同样会TLE。
但我们可以采用二分查找的思想去做,即找到区间k的首位第一次出现的位置。然后就可以求解了。这样可以时间复杂度为O(n*logn*m)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<queue>
#include<algorithm>
#include<functional>
#include<stack>
using namespace std;
const int MAX = 1000005;
int num[MAX];
int Deal1( int *num, int l, int r, int k, int len ){
if( l > r )
return -1;
int mid = (l + r) / 2;
if (num[mid] == k){
if ((mid - 1 >= 0 && num[mid - 1] != k) ?? mid == 0){
return mid;
}
else{
r = mid - 1;
}
}
else if (num[mid] > k){
r = mid - 1;
}
else{
l = mid + 1;
}
Deal1(num, l, r, k, len);
}
int Deal2( int *num, int l, int r, int k, int len ){
if( l > r )
return -1;
int mid = (l + r) / 2;
if (num[mid] == k){
if ((mid + 1 <= len - 1 && num[mid + 1] != k) ?? mid == len - 1){
return mid;
}
else{
l = mid + 1;
}
}
else if (num[mid] > k){
r = mid - 1;
}
else
l = mid + 1;
Deal2(num, l, r, k, len);
}
int GetOrder(int *num, int l, int r, int k, int n ){
int ans = 0;
if (num != NULL && n > 0){
int First = Deal1( num, 0, n - 1, k, n );
int Last = Deal2( num, 0, n - 1, k, n );
if (First != -1 && Last != -1){
ans = Last - First + 1;
}
}
return ans;
}
int main(){
int n, m, k;
while(scanf( "%d", &n ) != EOF ){
for (int i = 0; i < n; ++i){
//cin >> num[i];
scanf( "%d", &num[ i ] );
}
scanf( "%d", &m );//cin >> m;
while (m--){
scanf( "%d", &k );//in >> k;
int ans = GetOrder( num, 0, n, k, n );
//cout << ans << endl;
printf( "%d\n", ans );
}
}
return 0;
}