剑指Offer - 九度1371 - 最小的K个数
2013-11-23 15:45
- 题目描述:
-
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
- 输入:
-
每个测试案例包括2行:
第一行为2个整数n,k(1<=n,k<=200000),表示数组的长度。
第二行包含n个整数,表示这n个数,数组中的数的范围是[0,1000 000 000]。
- 输出:
-
对应每个测试案例,输出最小的k个数,并按从小到大顺序打印。
- 样例输入:
-
8 4 4 5 1 6 2 7 3 8
- 样例输出:
-
1 2 3 4
题意分析:
给定n个数,求出其中最小的k个数,n和k的范围都是20万。基本思路如下:
1. 全排序n个数,然后顺着数出k个,时间复杂度O(n * log(n) + k),空间复杂度O(1)。
2. 用最大堆来存k个数,剩下的n-k个数每个都和堆顶比较,比堆顶小时,就把堆顶元素替换掉。采取先pop()后push()的方法替换。时间复杂度O(n * log(k)),空间复杂度O(k)。
3. 直接统计每个值出现的次数,要么用hash,要么用map。然后数出最小的k个值即可。时间复杂度O(n),空间复杂度O(S),S为数组元素的取值范围。
根据此题的数据范围,n、k的大小不固定,可能很接近也可能差很远,所以方法1和方法2都可行。n和k接近时适合方法1,相差很远时适合方法2。方法3由于数组元素的取值范围太大,不可行。
对于方法3,一个很适用的问题,就是统计高考分数和排名。总共750分满分,一个省几十万考生,用桶排序的思想很容易就能算出某分数的全省排名。
1 // 652996 zhuli19901106 1371 Accepted 点击此处查看所有case的执行结果 1796KB 931B 950MS 2 // 201311180313 3 #include <cstdio> 4 #include <queue> 5 #include <vector> 6 using namespace std; 7 8 int main() 9 { 10 // min heap 11 priority_queue<int, vector<int>, less<int> > pq; 12 vector<int> vv; 13 int n, k; 14 int i, tmp; 15 16 while(scanf("%d%d", &n, &k) == 2){ 17 while(!pq.empty()){ 18 pq.pop(); 19 } 20 vv.clear(); 21 22 if(k > n){ 23 k = n; 24 } 25 for(i = 0; i < k; ++i){ 26 scanf("%d", &tmp); 27 pq.push(tmp); 28 } 29 30 for(i = k; i < n; ++i){ 31 scanf("%d", &tmp); 32 if(tmp < pq.top()){ 33 pq.pop(); 34 pq.push(tmp); 35 } 36 } 37 38 while(!pq.empty()){ 39 vv.push_back(pq.top()); 40 pq.pop(); 41 } 42 43 for(i = (int)vv.size() - 1; i >= 0; --i){ 44 if(i == (int)vv.size() - 1){ 45 printf("%d", vv[i]); 46 }else{ 47 printf(" %d", vv[i]); 48 } 49 } 50 printf("\n"); 51 vv.clear(); 52 } 53 54 return 0; 55 }