64 - 数据流中的中位数 || STL 堆

题目:

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


思路解析

数据是从数据流读出,因此数组的个数是再逐渐的增加。如何选用一个容器,能够存储数据,并能够给出中位数。

  • 无序数组:插入O(1) partation操作找出中位数 O(n)
  • 有序数组:插入O(n) 找出中位数O(1)
  • 有序链表:插入O(n) 找出中位数O(1)
  • 搜索二叉树:插入O(logn)~O(n),找出中位数O(logn)~O(n)
  • AVL:插入O(logn),在节点中添加计数信息,找出中位数O(1)

如果将容器中的数据按照顺序排列,那么最中间的两个数或者一个数,可以将数据平均分成2部分。

那么以偶数个数据节点为例:最中间的两个数中的左侧数字,是前一半数据的最大值;中间数右侧数字,是后一半数据的最小值。因此只要保证,数据能够平均分成2部分,前一部分都小于后一部分,那么中位数是前一部分的最大值和后一部分的最小值的平均值。

为了实现O(1)得到最大最小值,采用最大堆保存前一半数据,最小堆保存后一半数据

为了实现平均:当容器中数据个数是偶数:插入到最小堆;总数是奇数,插入到最大堆。

当总数是偶数时,应该插入最小堆,但如果此时的数据小于最大堆的最大值,即按值大小来说,它应该属于前一部分(最大堆);因此我们将该值插入到最大堆,然后取出最大堆中的最大值,插入到最小堆。

同理,奇数时,数据大于最小堆的最小值,也应该先如最小堆,将最小堆中的最小值,移动到最大堆。

关于 C++ STL 堆的知识:

heap 的头文件 #include<algorithm>

注意:_First, _Last 是容器的迭代器,如vector。

参考:c++中STL之heap, priority_queue使用

1. make_heap

make_heap(_First, _Last)
make_heap(_First, _Last, _Comp) // 对2个迭代器之间的元素建堆

默认是建立最大堆的。对int类型,可以在第三个参数传入greater<int>()得到最小堆

2. push_heap

push_heap(_First, _Last) // 首先在容器中添加一个元素,然后push_heap,处理最后添加的一个元素

新添加一个元素在末尾,然后重新调整堆序。也就是把元素添加在底层vector的end()处。每次只能处理 1 个。

3. pop_heap

pop_heap(_First, _Last) // 先pop_heap,然后在容器中pop

这个算法跟push_heap类似,参数一样。不同的是我们把堆顶元素取出来,放到了数组或者是vector的末尾,用原来末尾元素去替代,然后end迭代器减1

4. sort_heap

.sort_heap(_First, _Last) // 堆排序

template<class T>
class DynamicArray {
private:
    vector<T> max; // 数组的前一半一半最大堆,最大堆中的数全部小于最小堆
    vector<T> min; // 数组的后一半构成最小堆,
public:
    void Insert(T num) {
        // 已有元素总数是偶数,插入最小堆
        if (((min.size() + max.size()) & 0x01) == 0) {
            if (max.size() > 0 && num < max[0]) {
                // num 小于最小堆的最小值,按值看应该插入最大堆,因此,先插入max,将max中的最大值,移动到min
                max.push_back(num);
                push_heap(max.begin(), max.end(), less<T>());
                num = max[0]; // 取出最大堆的最大值,插入到最小堆
                pop_heap(max.begin(), max.end(), less<T>());
                max.pop_back();
            }
            min.push_back(num);
            push_heap(min.begin(), min.end(), greater<T>());
        } else {
            // 已有元素总数是奇数,插入最大堆
            if (min.size() > 0 && num > min[0]) {
                // num 大于最大堆的最大值,按值看应该插入最小堆
                min.push_back(num);
                push_heap(min.begin(), min.end(), greater<T>());
                num = min[0];
                pop_heap(min.begin(), min.end(), greater<T>());
                min.pop_back();
            }
            max.push_back(num);
            push_heap(max.begin(), max.end(), less<T>());
        }
    };
    T GetMedian() {
        int size = min.size() + max.size();
        if (size == 0)
            throw exception("No numbers are avliable");
        if ((size & 0x01) == 1)
            return min[0];
        else
            return (min[0]+max[0])/2;
    }
};

测试案例:《剑指offer》 GitHub

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-20 11:14:28

64 - 数据流中的中位数 || STL 堆的相关文章

《剑指offer》:[64]数据流中的中位数

题目:如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值.如果从数据流中读出偶数个数值,那么中位数就是所有数据排序后中间两个数的平均值. 例如:1,2,3,4,5的中位数为:3.1,2,3,4的中位数为:(2+3)/2=3. 方案一:采用Partition来解决.在[29]中我们讲过,快速查找中的Partition函数是十分重要,是一个比较常用的算法.所以这里我们采用partion函数来解决.从字符流里读字符,插入到一个无需的数组中的复杂度为

64 数据流中的中位数

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值.如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值. java: 1 import java.util.PriorityQueue; 2 import java.util.Comparator; 3 public class Solution { 4 /* 大顶堆,存储左半边元素 */ 5 private PriorityQueue<Integer> left

[剑指offer] 41. 数据流中的中位数 (大小堆,优先队列)

对于海量数据与数据流,用最大堆,最小堆来管理. class Solution { public: /* * 1.定义一个规则:保证左边(大顶堆)和右边(小顶堆)个数相差不大于1,且大顶堆的数值都小于等于小顶堆的数 * 2.大小堆顶可以用优先序列实现 插入规则: 当插入数值小于左边的堆顶时候,就插入左边,否则插入右边堆.(注意初始为空时,插入不能比较) 调整使得满足个数差<=1: 正常时是只有两种情况:p=q或者p=q+1,由于每插一个值就会考虑调整,那么边界情况就是p=q+2或者p+1=q p=

剑指offer:数据流中的中位数(小顶堆+大顶堆)

1. 题目描述 /** 如何得到一个数据流中的中位数? 如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值. 如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值. 我们使用 Insert()方法读取数据流,使用 GetMedian()方法获取当前读取数据的中位数. */ 2. 思路 /** 最大堆和最小堆 * 每次插入小顶堆的是当前大顶堆中最大的数 * 每次插入大顶堆的是当前小顶堆中最小的数 * 这样保证小顶堆中的数永远大于等于大顶堆中的数(值

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

题目描述: 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值.如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值. 本题最开始简单的理解为求中位数,使用的是快排的思想,当数据元素为奇数个时,求第n/2大的数,当元素个数为偶数时,先求n/2个数,然后对右边的求出一个最小值. 看了别人的做法,发现应该把这道题理解为一个在线算法题.关键是使用两个堆,最大化堆存储前n/2个数,最小化堆存储后n/2个数,当元素个数为偶数个

数据流中的中位数

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值.如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值. import java.util.*; public class Solution { private int count = 0; private PriorityQueue<Integer> minHeap = new PriorityQueue<>(); private PriorityQu

《剑指offer》数据流中的中位数

[ 声明:版权所有,转载请标明出处,请勿用于商业用途.  联系信箱:[email protected]] 题目链接:http://www.nowcoder.com/practice/9be0172896bd43948f8a32fb954e1be1?rp=4&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking 题目描述 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后

62 数据流中的中位数

题目描述 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值.如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值. 思路:这道题用到两个堆,使用优先级队列进行实现,原始的优先级队列是最大堆,加上greater仿函数之后是最小堆(加上functional头文件) 一定要保证前面的最大堆元素多于后面的最小堆元素,首先将元素压入最大堆,然后取出最小的元素压入最小堆,然后判断两者大小,最大堆数目小于最小堆,流需要压一个最小

[剑指offer] 63. 数据流中的中位数

题目描述 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值.如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值.我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数. 用一个大顶堆和一个小顶堆,维持大顶堆的数都小于等于小顶堆的数,且两者的个数相等或差1.平均数就在两个堆顶的数之中. 原文地址:https://www.cnblogs.com/ruoh3kou/p/10261