剑指offer: 数据流中的中位数

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

pop_heap、push_heap

分析:
  1. 数组是最简单的数据容器,如果数组没有排序,可以使用partition函数
     找到数组的中位数。在没有排序的数组中插入和找到一个中位数的时间
     复杂度是 O(1) 和 O(n)
     
     还可以往数组中插入新数据时让数组保持排序。由于可能需要移动 O(n)个、
     数,因此需要 O(n) 时间才能完成插入操作。在已经排好序的数组中找到
     一个中位数是一个简单的操作,只需要O(1)的时间。
     
  2. 排序的链表是另外一种选择。我们需要 O(n) 的时间才可以在链表中找到
     合适的位置插入新的数据。可以在 O(1) 的时间内得出中位数。
     
  3. 二叉搜索树可以将插入新数据的平均时间降低到 O(logn)。但是当二叉
     搜索树极度不平衡时看起来像一个排序的链表,插入新数据的时间仍然是
     O(n)。为了得到中位数,可以在二叉树节点中添加一个表示子树结点数目的
     字段。有了这个字段,可以在平均 O(logn) 的时间内得到中位数,但最差情况
     仍然需要 O(n) 时间。
     
  4. 为了避免二叉搜索树的最差情况,还可以使用平衡的二叉搜索树,即 AVL 树。
     通常 AVL 树的平衡因子是左右子树的高度差。可以稍作修改,将 AVL 的平衡
     因子改为左右子树的结点数目之差。可以用 O(logn) 的时间往 AVL 树中添加
     一个新的结点,同时用 O(1) 的时间得到所有结点的中位数。
     
  5. 如果数据在容器中已经排好序,那么中位数可以由 P1 和 P2指向的数得到。如果
     容器中数据的数目是奇数,那么 P1和 P2指向同一个数据。
     我们注意到整个数据容器被分割成两个部分。位于容器左半边的数据比右边的数据
     小。另外, P1指向的数据是左边部分最大的数, P2指向的是数据是右边部分最小
     的数,
     
     如果能保证数据容器左边的数据都小于右边的数据,即使左右两边的数据都没有
     排序,也可以根据左边最大的数和右边最小的数得到中位数。
     
     使用最大堆和最小堆来实现:
     首先保证数据平均分配到两个堆中,因此两个堆中数据之差不能超过1.为了实现
     平均分配,可以在数据的总数目是偶数时把数据插入最小堆,否则插入最大堆。
     还要保证最大堆中的所有数据都要小于最小堆中的数据。
     当数据总数是偶数时,应当插入最小堆,但是如果此时这个新的数据比最大堆的
     一些数据要小:可以先将新的数据插入最大堆,接着将最大堆中的数字拿出来插
     入最小堆。由于插入最小堆的数字是原最大堆中最大的数字,这样就保证了最小
     堆中的所有数字都大于最大堆中的数字。

class Solution {
public:
    void Insert(int num)
    {
    	if (((max.size() + min.size()) & 1) == 0) // 偶数应该插入最小堆
	{
	    if (max.size() > 0 && max[0] > num){
	    // 将num先插入大顶堆
	    max.push_back(num);
	    push_heap(max.begin(), max.end(), less<int> ());
				
	    // 将大顶堆中的最大元素插入小顶堆
	    num = max[0];
				
	    // 将该元素从大顶堆删除
	    pop_heap(max.begin(), max.end(), less<int> ());
	    // pop_heap 会将该元素放到最后一位 
	    max.pop_back(); 
	    } 
	    // 将num插入最小堆
	    min.push_back(num);
	    // 建立最小堆
	    push_heap(min.begin(), min.end(), greater<int> ()); 
	}
        else // 奇数应该插入最大堆 
	{
	    if (min.size() > 0 && min[0] < num){
	    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(), less<int> ());
        } 
    }
    double GetMedian()
    { 
    	int size = max.size() + min.size();
    	double result;
    	double big = max[0];
    	double small = min[0];
    	if ((size % 2) == 1) result = small;
    	else result = (big + small) / 2;
    	return result;
    }
private:
    vector<int> max;   // max为大顶堆 
    vector<int> min;   // min为小顶堆  
};

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用南:为帮助用户更好地理解和使用这些ASP项目,每个资源包都包含项目的演示文件和使用南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值