解题报告 中位数

题目来源

洛谷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.	}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

没头发的米糊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值