剑指41:数据流中的中位数
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
例如,
[2,3,4] 的中位数是 3
[2,3] 的中位数是 (2 + 3) / 2 = 2.5
设计一个支持以下两种操作的数据结构:
void addNum(int num) - 从数据流中添加一个整数到数据结构中。
double findMedian() - 返回目前所有元素的中位数。
有几种方法可以选取:
1.数组
如果插入时排序,时间复杂度为n,获取中位数的时间复杂度为1
如果插入时不排序,则时间复杂度为1,获取中位数时间复杂度为n
2.链表
排序的链表,插入时间复杂度为n,获取时间复杂度为1(用指针记录中位数位置)
3.二叉树
使用二叉搜索树,插入平均logn,最差n,查找时间同样
AVL树插入时间复杂度为logn,查找为1
但是这两个太复杂,不适合面试使用
4.最大堆和最小堆
将数组分为两部分,前半部分用最大堆,后半部分用最小堆,这样可以在logn的时间内插入,而且在1时间内获取到中位数
如果当前长度为偶数,就加入到最小堆;如果是奇数,就加入到最大堆,保证两个堆节点数量差不超过1
但是要保证最大堆的数据都小于等于最小堆的数据。
如果需要插入到最大堆,但是数据大于最小堆的根节点,即最小堆中最小的值,那么就先把该数加入到最小堆中,然后获取最小堆的根节点,加入到最大堆中。
反过来同理。
使用vector存储数据,用push_heap和pop_heap调整堆。
最大堆时push第三个参数为less<T>(),最小堆为greater<T>(),默认为最大堆。
1 class MedianFinder { 2 private: 3 vector<int> min; 4 vector<int> max; 5 public: 6 7 /** initialize your data structure here. */ 8 MedianFinder() { 9 cout<<"created medianfinder"<<endl; 10 } 11 12 void addNum(int num) { 13 if(!((min.size()+max.size())&1)){ 14 if(max.size()!=0 && num<max[0]){ 15 max.push_back(num); 16 push_heap(max.begin(),max.end(),less<int>()); 17 num=max[0]; 18 pop_heap(max.begin(),max.end(),less<int>()); 19 max.pop_back(); 20 } 21 min.push_back(num); 22 push_heap(min.begin(),min.end(),greater<int>()); 23 } 24 else{ 25 if(min.size()!=0 && num>min[0]){ 26 min.push_back(num); 27 push_heap(min.begin(),min.end(),greater<int>()); 28 num=min[0]; 29 pop_heap(min.begin(),min.end(),greater<int>()); 30 min.pop_back(); 31 } 32 max.push_back(num); 33 push_heap(max.begin(),max.end(),less<int>()); 34 } 35 } 36 37 double findMedian() { 38 if(min.size()==0) 39 return 0.0; 40 if(!((min.size()+max.size())&1)) 41 return (double)(min[0]+max[0])/2; 42 return min[0]; 43 } 44 }; 45 46 /** 47 * Your MedianFinder object will be instantiated and called as such: 48 * MedianFinder* obj = new MedianFinder(); 49 * obj->addNum(num); 50 * double param_2 = obj->findMedian(); 51 */