剑指Offer - 九度1349 - 数字在排序数组中出现的次数
2013-11-23 00:47
- 题目描述:
- 统计一个数字在排序数组中出现的次数。
- 输入:
-
每个测试案例包括两行:
第一行有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
题意分析:
题目要求在一个数组中找出一个数出现的次数,
第一种想法,当然是一次性全部数一遍,用map之类的结构保存统计结果,之后每次就可以log n复杂度查询了。时间复杂度为O(n) + m * O(log k),k是数组中不同元素的个数,空间复杂度O(k)。问题是——数组已经排序,这个特性没用到。
第二种想法,因为数组有序,所以每次都用二分查找找出一个数的左界和右界,然后right - left + 1即为结果。对于不存在的元素,二分当然返回-1之类的NOT_FOUND值,统计结果则为0。时间复杂度为m * O(log n),空间复杂度O(1)。
观察题目数据范围,数组长度n最大可以到10^6,查询次数m最大才10^3,如果采取上面方法一,O(n)则占了大头,不划算。所以选方法二。
其中bsearch_left求出target在数组a[]中的左下标,bsearch_right为右下标。这俩函数写法要小心,检查好数组指针,数据范围,左右端后才能开始二分查找。
查找到左右下标后,rr - ll + 1即为结果;查不到左右下标的话,结果为0。
1 // 652724 zhuli19901106 1349 Accepted 点击此处查看所有case的执行结果 4932KB 1913B 720MS 2 // 201311171831 3 #include <cstdio> 4 using namespace std; 5 6 int bsearch_left(const int a[], int n, int target) 7 { 8 int ll, rr, mm; 9 10 if(a == NULL || n <= 0){ 11 return -1; 12 } 13 14 if(target < a[0] || target > a[n - 1]){ 15 return -1; 16 } 17 18 // guarantee that a[left] < target && a[right] >= target 19 if(target == a[0]){ 20 return 0; 21 } 22 23 ll = 0; 24 rr = n - 1; 25 while(rr - ll > 1){ 26 mm = (ll + rr) / 2; 27 if(target > a[mm]){ 28 ll = mm; 29 }else{ 30 rr = mm; 31 } 32 } 33 34 if(a[rr] == target){ 35 return rr; 36 }else{ 37 return -1; 38 } 39 } 40 41 int bsearch_right(const int a[], int n, int target) 42 { 43 int ll, rr, mm; 44 45 if(a == NULL || n <= 0){ 46 return -1; 47 } 48 49 if(target < a[0] || target > a[n - 1]){ 50 return -1; 51 } 52 53 // guarantee that a[left] <= target && a[right] > target 54 if(target == a[n - 1]){ 55 return n - 1; 56 } 57 58 ll = 0; 59 rr = n - 1; 60 while(rr - ll > 1){ 61 mm = (ll + rr) / 2; 62 if(target >= a[mm]){ 63 ll = mm; 64 }else{ 65 rr = mm; 66 } 67 } 68 69 if(a[ll] == target){ 70 return ll; 71 }else{ 72 return -1; 73 } 74 } 75 76 int main() 77 { 78 int *a = NULL; 79 int i, n, m; 80 int target; 81 int ll, rr; 82 83 while(scanf("%d", &n) == 1){ 84 if(n <= 0){ 85 continue; 86 } 87 a = new int[n]; 88 for(i = 0; i < n; ++i){ 89 scanf("%d", &a[i]); 90 } 91 scanf("%d", &m); 92 for(i = 0; i < m; ++i){ 93 scanf("%d", &target); 94 ll = bsearch_left(a, n, target); 95 if(ll < 0){ 96 printf("0 "); 97 continue; 98 } 99 rr = bsearch_right(a, n, target); 100 if(rr < 0){ 101 printf("0 "); 102 continue; 103 } 104 if(rr >= ll){ 105 printf("%d ", rr - ll + 1); 106 }else{ 107 printf("0 "); 108 } 109 } 110 delete[] a; 111 a = NULL; 112 } 113 114 return 0; 115 }