【剑指offer】55.数据流中的中位数

总目录:

算法之旅导航目录

 

1.问题描述

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
数据范围:数据流中数个数满足 1<n<100,大小满足 1val1000 
进阶: 空间复杂度 O(n), 时间复杂度 O(nlogn)

 

2.问题分析

 用一个buffer来缓存已经接收到的数据,使用某种规则保证其有序,取中位数时直接按索引取即可,关键在于如何使其在满足时空复杂度的前提下有序。

多种解法

0链表

用链表来保存数据,不仅插入快,而且用两个指针维护中间点/中间两个点,可以实现O(n)的存入,O(1)的读取。

1插入排序

使用vector作buffer,每次插入新数据,然后向前插入到有序集合中

2堆排序

中位数是指:有序数组中中间的那个数。则根据中位数可以把数组分为如下三段:[中位数的左边,中位数,中位数的右边]。

使用一个大根堆保存左边较小的数据left_q,使用一个小根堆保存右边较大的数据right_q。

如果left_q.Size=right_q.Size+1,则中间值=left_q.Top;

如果left_q.Size=right_q.Size-1,则中间值=right_q.Top;

如果left_q.Size=right_q.Size,则中间值=(left_q.Top+right_q.Top)/2;

那么存在2个关键问题:

(1)如何维护这两个堆,进来的数值应该存到哪个堆中,

(2)要注意保持两个堆的平衡,两个堆的大小之差最大为1;

解决策略是先放到一个堆中,然后取其Top再放到另一个堆中,此时进行堆平衡、从过大的堆中取出元素放入较小的堆中直到达到允许的平衡状态(尺寸之差不超过1)。


3.代码实例

插入排序法

 1 class Solution {
 2   public:
 3     //维护一个堆
 4 #define SCD static_cast<double>
 5 
 6     vector<int> buffer;
 7     void Insert(int num) {
 8         //插入排序
 9         buffer.push_back(num);
10         int numSize = buffer.size();
11         for (int cur = numSize - 1; cur != 0; cur--) {
12             //停止插入
13             if (buffer[cur - 1] < buffer[cur]) {
14                 return;;
15             }
16 
17             //和前一个交换
18             swap(buffer[cur - 1], buffer[cur]);
19         }
20 
21     }
22 
23     double GetMedian() {
24         int sz = buffer.size();
25         if (sz & 1) {
26             return SCD(buffer[sz >> 1]);
27         } else {
28             return SCD(buffer[sz >> 1] + buffer[(sz - 1) >> 1]) / 2;
29         }
30         return 0;
31     }
32 
33 };
View Code

堆排序法

 1 class Solution {
 2   public:
 3 #define SCD static_cast<double>
 4     priority_queue<int> min_q; // 大顶推
 5     priority_queue<int, vector<int>, greater<int>> max_q; // 小顶堆
 6 
 7     void Insert(int num) {
 8 
 9         min_q.push(num); // 试图加入到大顶推
10 
11         //换出min_q中的最大值
12         max_q.push(min_q.top());
13         min_q.pop();
14 
15         //平衡两个堆
16         //这里保证了min_q和max_q的元素数量之差不会超过1
17         if (min_q.size() < max_q.size()) {
18             min_q.push(max_q.top());
19             max_q.pop();
20         }
21     }
22 
23     double GetMedian() {
24         //下面语句的意思是尽量保证min_q.Size>=max_q.Size,不允许min_q.Size<max_q.Size
25         return min_q.size() > max_q.size() ? SCD(min_q.top()) : SCD(
26                    min_q.top() + max_q.top()) / 2;
27     }
28 
29 };
View Code

 

posted @ 2022-12-01 19:57  啊原来是这样呀  阅读(44)  评论(0)    收藏  举报