剑指 Offer 41. 数据流中的中位数

题目描述

剑指 Offer 41. 数据流中的中位数

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

例如,

[2,3,4] 的中位数是 3

[2,3] 的中位数是 (2 + 3) / 2 = 2.5

设计一个支持以下两种操作的数据结构:

void addNum(int num) - 从数据流中添加一个整数到数据结构中。
double findMedian() - 返回目前所有元素的中位数。

示例 1:

输入:
["MedianFinder","addNum","addNum","findMedian","addNum","findMedian"]
[[],[1],[2],[],[3],[]]
输出:[null,null,null,1.50000,null,2.00000]
示例 2:

输入:
["MedianFinder","addNum","findMedian","addNum","findMedian"]
[[],[2],[],[3],[]]
输出:[null,null,2.00000,null,2.50000]

限制:

最多会对 addNum、findMedia进行 50000 次调用。

思路分析

思路一:

数组无序插入 取中位数时快速排序取中间元素

时间复杂度 插入元素O(1) 取出中位数O(n*logn)

思路二:

数组插入过程和直接插入排序类似,插入后即排好序

取中位数直接取中间元素

时间复杂度 插入元素O(n) 取出中位数O(1) O(1)

注:直接插入可以优化成折半插入,虽然元素移动次数一样都是n,但是比较次数折半更好

思路三:

每次插入数组完全有序是不必的,使用堆可以将插入元素的复杂度降为O(logn)

结构规则如下:

堆 数组分为两部分

(1)A为大顶堆 B为小顶堆

(2)A中元素值 均小于 B中元素值

(3)B比A多0或1个元素

以1,2,3,4,5插入顺序为例如下图说明:

avatar

总结:插入元素满足结构体可以查找中位数的形态。

对于向大数结合插入的所有情况如下:一开始 小数A(空集合) 大数B(空集合)

初始状态 集合中间状态 集合最终状态
插入1 A(空集合) B(1) A(空集合) B(1)
插入2 A(空集合) B(1,2) 个数差2 B给A一个数 A(1) B(2)
插入3 A(1) B(2,3) 符合规范 A(1) B(2,3)
插入4 A(1) B(2,3,4) 个数差2 B给A一个数 A(1,2) B(3,4)
插入0 A(1,2) B(3,4,0) A堆顶大于B的 B和A交换一下堆顶 A(0,1) B(2,3,4)
插入-1 A(0,1) B(2,3,4,-1) 个数差2且A堆顶大于B的 B给A一个 就可以了 (-1,0,1) B(2,3,4)

处理流程

1.直接插入B

2.判断个数是否差2,如果差就B堆顶给A

3.判断堆顶是否A的大于B的,如果A>B堆顶,A,B堆顶交换(但是要注意A集合为空无法访问堆顶的情况)

代码实现

初始版代码

class MedianFinder {
public:
    /** initialize your data structure here. */
    MedianFinder() {

    }
    
    void addNum(int num) {
        B.push(num);
        //堆顶大小不规范
        //集合元素个数差距为2
        if(A.size() == B.size()-2) {
            A.push(B.top());
            B.pop();
        }
        if(A.size() != 0 && A.top() > B.top()) { // 如果A.top为空时就不能判断就直接跳过即可

            int a_top = A.top();
            int b_top = B.top();
            A.pop();
            B.pop();
            B.push(a_top);
            A.push(b_top);
        }

        

    }
    
    double findMedian() {
        if(A.size() == B.size()) {
            return (A.top()+B.top())/2;
        }else {
            return B.top();
        }
    }
private:
    priority_queue<double, vector<double>, less<double>> A;
    priority_queue<double, vector<double>, greater<double>> B;

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

优化版代码

class MedianFinder {
public:
    /** initialize your data structure here. */
    MedianFinder() {

    }
    
    void addNum(int num) {
        B.push(num);
        A.push(B.top());
        B.pop();
        if(A.size() != B.size()) {
            B.push(A.top());
            A.pop();
        }
        

    }
    
    double findMedian() {

        return A.size() == B.size() ? (A.top()+B.top())/2 : B.top();
    }
private:
    priority_queue<double, vector<double>, less<double>> A;
    priority_queue<double, vector<double>, greater<double>> B;

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

猜你喜欢

转载自www.cnblogs.com/linxuesong/p/13184748.html