题目描述
如何得到一个数据流中的中位数?
如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。
如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
样例
Input:1, 2, 3, 4
Output:1,1.5,2,2.5
## 解释:每当数据流读入一个数据,就进行一次判断并输出当前的中位数。
解题思路
-
描述
我们定义一个count用于计数,一个val用于存储计数的数字。遍历数组,如果count为0,则将当前值赋值val,然后count++;如果count不为0,如果当前值等于val,则count++,否则count–。
本题目同样是使用大顶堆和小顶堆。
大顶堆和小顶堆的特点:每一次通过 l o g n log^n logn 时间可获取最大或最小值。
我们创建两个堆,一个大顶堆和一个小顶堆,分别维护一部分数据,且大顶堆数据量与小顶堆数据量相等或仅比小顶堆数据量大一。
假定数据流中有n个数,小顶堆维护数值较大的 n/2 个数,大顶堆维护数值较小的 n/2 个数,那么小顶堆的堆顶值和大顶堆的堆顶值则是数据流的中间数值。
简单如图示:
已经确定如何处理该问题。现在讨论如何维护大顶堆和小顶堆:
- 数据流中读入一个数,将其加入小顶堆中。如果小顶堆的顶堆值大于大顶堆的顶堆值,此时需要交换两者的值。
- 数据流中读入一个数,将其加入小顶堆。如果小顶堆的数据量比大顶堆的数据量大于一,此时应将小顶堆的顶堆值放到大顶堆中。
-
实现代码:
/* 包含头文件 #include <queue> */ priority_queue<int> big_heap; // 大顶堆 priority_queue<int, vector<int> ,greater<int> > small_heap; // 小顶堆 void insert(int num){ int tmp; big_heap.push(num); if((small_heap.size()) && (big_heap.top() > small_heap.top())) // 发生逆序 { tmp = small_heap.top(); small_heap.pop(); small_heap.push(big_heap.top()); big_heap.pop(); big_heap.push(tmp); } if(big_heap.size() - small_heap.size() > 1) { small_heap.push(big_heap.top()); big_heap.pop(); } } double getMedian(){ if((big_heap.size() + small_heap.size()) % 2) // 和为奇数 { return big_heap.top(); } else { return (big_heap.top() + small_heap.top()) / 2.0; } }