-
题目描述:
- 统计一个数字在排序数组中出现的次数。
-
输入:
-
每个测试案例包括两行:
第一行有1个整数n,表示数组的大小。1<=n <= 10^6。
第二行有n个整数,表示数组元素,每个元素均为int。
第三行有1个整数m,表示接下来有m次查询。1<=m<=10^3。
下面有m行,每行有一个整数k,表示要查询的数。
-
输出:
-
对应每个测试案例,有m行输出,每行1整数,表示数组中该数字出现的次数。
-
样例输入:
-
81 2 3 3 3 3 4 513
-
样例输出:
-
4
-
题目分析:
数字在排序数组中出现的次数,第一想法应该是用hash方法,计算出数组中所有数据出现的次数,然后直接查找,时间复杂度O(n),空间复杂度O(n)。但是这种方法未能利用该数组是排序的特点,所以有关排序的题目,要及时联想到二分查找。
本题就是利用的二分查找的一个变体,来求出要查找的数在数组中第一次出现和最后一次出现的位置,来确定数字在数组中出现的次数。
当然在具体写程序的过程还要注意一些临界条件的判断以及特殊情况的处理(如某个数出现的位置是0和在数组中未找到这个数字返回为0的时候区别,我程序中用了标记来区分这两种情况)。二分查找时间复杂度是O(logn),减少了时间复杂度。
//source:http://ac.jobdu.com/problem.php?pid=1349
#include <iostream>
#include <cstdio>
using namespace std;
bool flag1 = true;
bool flag2 = true;
int SearchFirst(int A[], int n, int value)
{
int left = 0, right = n - 1;
while (left <= right)
{
int mid = (left + right)/2;
if (A[mid] < value)
left = mid + 1;
else if (A[mid] > value)
right = mid - 1;
else
{
if (A[mid - 1] != value || mid == 0)//mid==0
return mid;
else
right = mid - 1;
}
}
flag1 = false;
return 0;
}
int SearchLast(int A[],int n, int value)
{
int left = 0, right = n - 1;
while (left <= right)
{
int mid = (left + right)/2;
if (A[mid] < value)
left = mid + 1;
else if (A[mid] > value)
right = mid - 1;
else
{
if (A[mid + 1] != value || mid == n)//mid==n
return mid;
else
left = mid + 1;
}
}
flag2 = false;
return 0;
}
int main()
{
int n, m;//n是数组的大小,m是查询的次数
int k;//要查询的数
while (scanf("%d",&n)!=EOF)
{
int *array = new int[n+1];
for (int i = 0; i < n; i++)
{
scanf("%d", array+i);
}
scanf ("%d",&m);
while (m--)
{
scanf("%d",&k);
int first =SearchFirst(array,n,k);
int last = SearchLast(array,n,k);
if (!flag1 && !flag2)
printf("0\n");
else
printf("%d\n",last - first + 1);
}
}
return 0;
}