解题报告 中位数
Posted 没头发的米糊
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了解题报告 中位数相关的知识,希望对你有一定的参考价值。
题目来源
洛谷P1168 中位数 - 洛谷
题目描述
给出一个长度为N的非负整数序列A[i],对于所有1 ≤ k ≤ (N + 1) / 2,输出A[1], A[3], …, A[2k - 1]的中位数。即前1,3,5,……个数的中位数。
输入输出格式
输入格式:
输入文件median.in的第1行为一个正整数N,表示了序列长度。
第2行包含N个非负整数A[i] (A[i] ≤ 10^9)。
输出格式:
输出文件median.out包含(N + 1) / 2行,第i行为A[1], A[3], …, A[2i – 1]的中位数。
输入输出样例
输入样例#1:
7
1 3 5 7 9 11 6
输出样例#1:
1
3
5
6
说明
对于20%的数据,N ≤ 100;
对于40%的数据,N ≤ 3000;
对于100%的数据,N ≤ 100000。
解题思路
本题的思路和题目A的思路可以说非常相似,都是处理动态变化数据大小关系。区别在于题目A是寻找最小的两个数,而本题是寻找中位数。如果要用堆,堆也只能轻松地取出最大或者最小的数,也没法取中间的数。这里就需要引入一个全新的概念——对顶堆。
附:对顶堆
如果把堆中的小根堆想成一个上宽下窄的三角形,把大根堆想成一个上窄下宽的三角形,那么对顶堆就可以具体地被想象成一个“陀螺”或者一个“沙漏”,通过 这两个堆的上下组合,我们可以把一组数据分别加入到对顶堆中的大根堆和小根堆,以维护我们不同的需要。 把小根堆放在大根堆上面,根据数学中的不等式原理,不难得到小根堆中的所有元素比大根堆中的所有元素都要大。 通过对两堆中元素个数的控制,就可以解决第K大元素的问题。 |
只需要控制好对顶堆,就能够迅速找到中位数,于是本题得解。
具体方法
对于本题,首先创建一个对顶堆,即一个大根堆和一个小根堆,全部采用STL中的优先队列完成。大根堆(从大到小)存较小的数,小根堆(从小到大)存较大的数。
一边读入新数一边处理,如果新数大于大根堆顶,就放入小根堆,否则放入大根堆。
然后,维护使两堆容量大小之差不大于1。
因为题目保证是求奇数个数的中位数,因此在每次读到奇数个数时就需要输出一次中位数,显然比中位数小的个数=比中位数大的个数,中位数要么在大根堆顶,要么在小根堆顶。所以取多一个数的堆顶即可。
接下来,使用样例数据进行具体演示。
7 1 3 5 7 9 11 6 |
最终输出结果如下:
1 3 5 6 |
通过上述演示,不难确定该方法可以正确解出题目。
对顶堆是朴素堆的一个变种,整体而言在求第K大的元素中经常被使用到,求中位数只是一个特殊情况。堆还有很多其他有用的变种,灵活使用堆这个数据结构可以解决很多问题。
代码实现
1. #include <iostream>
2. #include <queue>
3. #include <algorithm>
4. using namespace std;
5. priority_queue<int> q1;
6. priority_queue< int, vector<int> ,greater<int> > q2;
7. int main()
8. {
9. int n,x,i;
10. cin >> n;
11. cin >> x;
12. q1.push(x);
13. cout << x << endl;
14. for(i = 2 ; i <= n ; i ++)
15. {
16. cin >> x;
17. if(x > q1.top())
18. q2.push(x);
19. else
20. q1.push(x);
21. if( abs( (int)( q1.size() - q2.size() ) ) > 1 )
22. {
23. if(q1.size() > q2.size())
24. {
25. q2.push(q1.top());
26. q1.pop();
27. }
28. else
29. {
30. q1.push(q2.top());
31. q2.pop();
32. }
33. }
34. if(i % 2 != 0)//odd
35. {
36. if(q1.size() > q2.size())
37. {
38. cout << q1.top() << endl;
39. }
40. else
41. {
42. cout << q2.top() << endl;
43. }
44. }
45. }
46. return 0;
47. }
以上是关于解题报告 中位数的主要内容,如果未能解决你的问题,请参考以下文章