数据流中的中位数

目录

题目描述

思路

代码


题目描述

中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。

例如,

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

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

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

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

addNum(1)
addNum(2)
findMedian() -> 1.5
addNum(3) 
findMedian() -> 2
进阶:

如果数据流中所有整数都在 0 到 100 范围内,你将如何优化你的算法?
如果数据流中 99% 的整数都在 0 到 100 范围内,你将如何优化你的算法?

思路

这是一道超级经典的题目,相信老师讲数据结构"优先队列"时应该都讲了这一数据结构在中位数上的运用.我么为何会想到优先队列呢?

中位数,如果分为两段,前半段的top()一定是小于后半段的top(),我们分别设置一个大根堆一个小根堆,大根堆的top指向前半段的最大值,小根堆的top()指向后半段的最小值,每次加入num时动态调整两个数据结构,就能维持大根堆的top和小根堆的top的正确指向.

具体情况分为以下几类(规定大根堆为l,小根堆为r):

1.两个堆都是空的,我们直接插入到大根堆l.

2.大根堆l不是空的,小根堆r是空的.

        ①.当num>l.top时,直接r.push(num)

        ②.当num<=l.top时,我们先让l.top()出来(pop操作前使用temp保存),再令r.push(temp),l.push(num),维持l.top()<r.top()且l的所有节点都小于等于r.top()的规定.

3.都不是空且l.size()>r.size()

        ①.num>=r.top(),直接r.push(num)

        ②.<一>num>=l.top(),直接r.push(num)

             <二>num<l.top(),我们暂存l.top(),l.pop(),r.push(temp),l.push(num)

4.都不是空且l.size()==r.size()

        ①.num<=l.top(),直接l.push(num)

        ②.<一>num>l.top()&&num<=r.top(),也是l.push(num)

             <二>num>r.top()时,l.push(r.top()).  r.pop();  r.push(num)

:在考虑push到哪里时要注意,我们要考虑num==l,top()或者num==r.top()时到底应该在size1<size,size1==size2排列组合的情况,多想一想,拿笔模拟一下就会明白很多.上述思路相对于题解虽然显得啰嗦,但是也比题解更容易理解一些.

最后附上AC代码:

代码


class MedianFinder {
public:
    /** initialize your data structure here. */
    priority_queue<int, vector<int>, greater<int>> r;       //小根堆
    priority_queue<int, vector<int>, less<int>> l;           //大根堆

    MedianFinder() {}

    void addNum(int num) {
        int size1 = l.size();
        int size2 = r.size();
        int temp;
        if (size1 > size2) {        //奇数情况,插入变偶数
            if (size2 == 0) {
                temp = l.top();
                if (temp > num) {   //右为空
                    l.pop();
                    l.push(num);
                    r.push(temp);
                }
                else r.push(num);
            }
            else {
                if (num >= r.top()) {
                    r.push(num);
                }
                else if (num < r.top()) {
                    if (num > l.top()) {
                        r.push(num);
                    }
                    else {
                        temp = l.top();
                        l.pop();
                        l.push(num);
                        r.push(temp);
                    }
                }
            }
            
        }
        else if (size1==size2&&size1==0) {
            l.push(num);
        }
        else if(size1==size2&&size1!=0){                      //偶数情况,插入变奇数
            if (num <= l.top()) {
                l.push(num);
            }
            else {
                if (num <= r.top()) {
                    l.push(num);
                }
                else {
                    temp = r.top();
                    r.pop();
                    l.push(temp);
                    r.push(num);
                }
            }
        }
    }

    double findMedian() {
        int size1 = l.size();
        int size2 = r.size();
        if (size1 > size2) {
            return l.top();
        }
        return ((double)l.top() + (double)r.top())/2.0;
    }
};

小结 

中位数相关的题目我们要联想到优先队列,在不同情况下的模拟要仔细详尽. 

计算数据流中位数可以通过Flink的ProcessFunction来实现。 具体实现步骤如下: 1. 将数据流按照大小排序 2. 计算数据流的长度,如果是奇数,则中位数为第 (length+1)/2 个元素;如果是偶数,则中位数为第length/2个元素和第(length/2+1)个元素的平均值。 3. 在ProcessFunction的实现,可以使用状态变量来保存数据流的有序列表,并计算中位数。 以下是一个简单的示例代码: ```java public class MedianFunction extends ProcessFunction<Integer, Double> { private ListState<Integer> values; @Override public void open(Configuration parameters) throws Exception { super.open(parameters); values = getRuntimeContext().getListState(new ListStateDescriptor<Integer>("values", Integer.class)); } @Override public void processElement(Integer value, Context ctx, Collector<Double> out) throws Exception { values.add(value); List<Integer> sortedValues = new ArrayList<>(); for (Integer v : values.get()) { sortedValues.add(v); } Collections.sort(sortedValues); int length = sortedValues.size(); if (length % 2 == 0) { double median = (sortedValues.get(length/2) + sortedValues.get(length/2 - 1)) / 2.0; out.collect(median); } else { double median = sortedValues.get(length/2); out.collect(median); } } } ``` 在上述代码,我们使用了ListState来保存数据流的元素,并在每次处理新元素时重新排序并计算中位数。注意,这只是一个简单的示例,实际应用需要考虑更多的问题,比如数据倾斜、数据丢失等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Shallow_Carl

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值