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

题目链接:https://leetcode-cn.com/problems/shu-ju-liu-zhong-de-zhong-wei-shu-lcof/

没有排序的数组

同面试题39题,利用分治的思想找出数组的中位数

插入 找中位数
时间复杂度 O(1) O(n)

排序数组

利用一个排序的数组,数字进来以后插入到合适的位置,时间复杂度O(n)

插入 找中位数
时间复杂度 O(n) O(1)

排序链表

利用两个指针分别指向链表的中间节点,链表中有偶数个数,指向同一个节点;奇数个数,指向不同节点;

插入 找中位数
时间复杂度 O(n) O(1)

二叉搜索树

二叉搜索树可以把插入新数据的平均时间降到O(logn);但是当二叉搜索树极度不平衡时,就退化成了一个排序的链表,插入新数据的时间复杂度仍然是O(n);为了得到中位数,可以再二叉树的节点中添加一个表示子树节点数目的字段,通过这个字段,可以再平均O(logn)时间内找到中位数,但是最差情况仍需要O(n)

插入 找中位数
时间复杂度 平均O(logn) 最差O(n) 平均O(logn) 最差O(n)

平衡的二叉搜索树

插入 找中位数
时间复杂度 O(logn) O(1)

最大堆和最小堆

如果容器中的数字已经排好序了,p1和p2分别指向中间的节点;奇数时指向同一个节点,偶数时指向不同节点;然后我们将容器分割成两部分,0~p1是一部分 p2~n是一部分;可以发现p1是左边的最大值,p2是右边的最小值;只要能保证,容器左边的数据都小于右边的数据,即使两边的数据没有排序,也可以根据左边最大的数以及右边最小的数得到中位数;

因此,可以维护一个最大堆,维护一个最小堆;用最大堆实现左边数据容器,用最小堆实现右边数据容器;

插入 找中位数
时间复杂度 O(logn) O(1)

所有数维护成两个集合,小根堆和大根堆;大根堆和小根堆的堆顶为数据流的中间的两个数;规定,如果有奇数个数,小根堆比大根堆多一个数;

  1. 每次将要插入的数,插入到大根堆中
  2. 如果大根堆堆顶的元素比小根堆堆顶元素大,那么将大根堆堆顶元素弹出,放入小根堆;将小根堆堆顶元素弹出,放入大根堆;
  3. 如果大根堆的元素数比小根堆的元素数多2,那么将大根堆的堆顶弹出,放入小根堆;
class MedianFinder {
public:
    /** initialize your data structure here. */
    MedianFinder() {

    }
    priority_queue<int> max_heap;
    priority_queue<int, vector<int>, greater<int>> min_heap;
    void addNum(int num) {
        //当前元素插入到大根堆;
        max_heap.push(num);
        //如果小根堆堆顶元素小于大根堆堆顶元素
        while(min_heap.size() && min_heap.top() < max_heap.top())
        {
            //小根堆和大根堆堆顶元素交换
            auto minv =min_heap.top(), maxv = max_heap.top();
            min_heap.pop(), max_heap.pop();
            max_heap.push(minv), min_heap.push(maxv);
        }
        //如果大根堆的元素数目比小根堆元素数目多2及以上
        if(max_heap.size() > min_heap.size() + 1)
        {
            //将大根堆的元素弹出,放入小根堆
            min_heap.push(max_heap.top());
            max_heap.pop();
        }
    }
    
    double findMedian() {
        //奇数个数
        if(max_heap.size() + min_heap.size() & 1) return max_heap.top();
        //偶数个数
        return (max_heap.top() + min_heap.top()) / 2.0;
    }
};

/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder* obj = new MedianFinder();
 * obj->addNum(num);
 * double param_2 = obj->findMedian();
 */

posted @ 2020-05-03 19:46  NaughtyCoder  阅读(105)  评论(0)    收藏  举报