剑指offer(C++)——数据流中的中位数

来源:互联网 发布:如何投诉淘宝客服态度 编辑:程序博客网 时间:2024/06/05 12:41

题目描述

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

思路:由于数据是从数据流中读取出来的,数据的个数随着时间的变化而增加。因此我们需要一个数据容器来保存读取出来的数据。

解法1:用数组来保存数据。如果数组无序,则找到中位数最优的算法时间复杂度为O(n),插入一个数字的时间复杂度为O(1);

解法2:还可以让数组一直保持有序,有可能需要移动O(n)个数,因此插入一个数字的时间复杂度为O(n),找到中位数只需O(1)时间即可完成;

解法3:用排序的链表来保存数据。插入一个数字需要O(n)时间,如果定义两个指针指向链表中间的结点,找到中位数只需O(1)时间;

解法4:用二叉搜索树保存数据。插入和查找中位数平均时间可以做到log(n),最坏情况下同样需要O(n)时间;

解法5:用AVL树。稍微修改可以做到用O(logn)时间插入一个数字,O(1)时间得到中位数;

如果数据在容器中已经排好序,那么中位数可以有P1和P2指向的数得到。如果数据个数为奇数个,则P1和P2指向同一个数。


通过观察发现,整儿数据容器被分隔成两部分,位于容器左边的数比右边的数小,而且,P1指向的数据是左边部分最大数,P2指向的数据是右边部分最小数。这一发现很重要,即使P1左边和P2右边的数据没有排序,我们也可以根据左边最大值和右边最小值来得到中位数。那么如何才能快速得到一个容器中的最大值和最小值呢?我们先到了大顶堆和小顶堆。

解法6:用大顶堆实现左边的的数据容器,小顶堆实现右边的数据容器。往堆中插入一个数据时间复杂度为O(logn),而得到最大值(或最小值)只需O(1)时间。有一点需要注意,我们要保证数据被平均分配到两个堆中(想想为什么?),因此两堆大小之差不能超过1。实现方法是数据流中奇数位的数插入小顶堆中,偶数位的数插入大顶堆中。同时还要保证大顶堆中的所有数都要小于小顶堆中的数。考虑一种特殊情况,例如,插入偶数位的数,按照分配规则应该插入大顶堆中,但是这个数大于小顶堆的最小值怎么办?如果直接插入大顶堆,就不符合大顶堆所有值都小于小顶堆的值这一条件。可以这样解决:想把该数插入小顶堆,然后进行堆排序,接着讲小顶堆的最小值拿出来插入到大顶堆中,这样就满足所有条件了。同理插入奇数位的数时也要考虑这种特殊情况。

代码实现如下:

class Solution {public://插入原则:奇数位的数插入小顶堆,偶数位数的插入大顶堆,除特殊情况外void Insert(int num){/*特殊情况1:当插入奇数位的数num时,应插入小顶堆里,但是当num小于大顶堆的最大值时,应将其插入大顶堆,然后将大顶堆的最大值插入小顶堆中*/if (((min.size() + max.size()) & 1) == 0)                   {if (max.size() > 0 && num < max[0]){max.push_back(num);push_heap(max.begin(), max.end());              //进行堆排序num = max[0];                                   //找到最大值pop_heap(max.begin(), max.end());               //将最大值移除堆(其实就是将最大值放在数组最后一位)max.pop_back();                                 //删除}min.push_back(num);push_heap(min.begin(), min.end(),greater<int>());       //构建小顶堆}else{/*特殊情况2:当插入偶数位的数num时,应插入大顶堆里,但是当num大于小顶堆的最小值时,应将其插入小顶堆,然后将小顶堆的最小值插入大顶堆中*/if (min.size() > 0 && num > min[0]){min.push_back(num);push_heap(min.begin(), min.end(),greater<int>());             //进行排序num = min[0];                                                 //找到最小值pop_heap(min.begin(), min.end(),greater<int>());              //将最小值移除堆(同上)min.pop_back();                                               //删除}max.push_back(num);push_heap(max.begin(), max.end());}}double GetMedian(){int size = min.size() + max.size();if (size == 0)return 0;if ((size & 1) == 1)return min[0];elsereturn (double)(max[0] + min[0]) / 2;}private:vector<int> min;                 //用来模拟小顶堆vector<int> max;                 //用来模拟大顶堆};

关于heap算法可参考:http://blog.csdn.net/yf_li123/article/details/70242850


0 0