数据流的中位数 - LeetCode 热题 76

大家好!我是曾续缘😙

今天是《LeetCode 热题 100》系列

发车第 76 天

堆第 3 题

❤️点赞 👍 收藏 ⭐再看,养成习惯

数据流的中位数

中位数是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值。

  • 例如 arr = [2,3,4] 的中位数是 3 。
  • 例如 arr = [2,3] 的中位数是 (2 + 3) / 2 = 2.5

实现 MedianFinder 类:

  • MedianFinder() 初始化 MedianFinder 对象。

  • void addNum(int num) 将数据流中的整数 num 添加到数据结构中。

  • double findMedian() 返回到目前为止所有元素的中位数。与实际答案相差 10-5 以内的答案将被接受。

示例 1:

输入
["MedianFinder", "addNum", "addNum", "findMedian", "addNum", "findMedian"]
[[], [1], [2], [], [3], []]
输出
[null, null, null, 1.5, null, 2.0]
解释
MedianFinder medianFinder = new MedianFinder();
medianFinder.addNum(1);    // arr = [1]
medianFinder.addNum(2);    // arr = [1, 2]
medianFinder.findMedian(); // 返回 1.5 ((1 + 2) / 2)
medianFinder.addNum(3);    // arr[1, 2, 3]
medianFinder.findMedian(); // return 2.0

提示:

  • -105 <= num <= 105
  • 在调用 findMedian 之前,数据结构中至少有一个元素
  • 最多 5 * 104 次调用 addNum 和 findMedian
难度:💝💝💝

解题方法

这个题目要求我们实现一个数据流的中位数 finder,即 MedianFinder 类。该类需要维护一个数据流,并能快速地找到目前为止所有元素的中位数。

因为中位数把数据分成两个大小相等的集合,其中一个集合的数都小于等于中位数,另一个集合的数都大于等于中位数。如果数据量为偶数,则是两个数把数据分成两个大小相等的集合,中位数就是两个数的平均。

我们使用两个优先队列来维护数据流,一个用来维护小于等于中位数的集合(命名为 l),另一个用来维护大于等于中位数的集合(命名为 r),我们该使用大根堆还是小根堆?

如果中位数在l集合中,那它就是l集合的最大值,因此l集合使用大根堆。

如果中位数在r集合中,那它就是r集合的最小值,因此r集合使用小根堆。

有了两个堆之后,我们需要时刻保证中位数在堆顶,也就是说lr集合的数量需要尽可能平均。

1. MedianFinder 类的初始化

MedianFinder() 初始化 MedianFinder 对象。这里我们使用两个优先队列,一个用于存储较大的数(命名为 r),另一个用于存储较小的数(命名为 l)。

class MedianFinder {
    PriorityQueue<Integer> l; // 用于存储较小的一半
    PriorityQueue<Integer> r; // 用于存储较大的一半
    public MedianFinder() {
        l = new PriorityQueue<Integer>((a, b) -> (b - a));
        r = new PriorityQueue<Integer>();
    }
    // ... 其他方法
}

2. addNum 方法

void addNum(int num) 将数据流中的整数 num 添加到数据结构中。这个方法是这个类的主要方法,确保了数据流的中位数可以被快速找到。

  • 如果 l 是空的,或者 num 小于等于 l 的顶部元素,那么 num 会被加入到 l 中。
  • 然后,如果 l 的数量大于 r 的数量加 1,那么 l 的顶部元素会被转移到 r 中,以保证 l 和 r 的数量接近。
  • 如果 num 大于 l 的顶部元素,那么 num 会被加入到 r 中。
  • 然后,如果 r 的数量大于 l 的数量,那么 r 的顶部元素会被转移到 l 中。
    这样,我们就可以确保 l 和 r 的数量相差不超过 1,而中位数就是 l 的顶部元素(如果 l 的数量大于 r 的数量)或者 l 和 r 顶部元素的平均值(如果 l 和 r 的数量相等)。
public void addNum(int num) {
    // 如果 l 是空的,或者 num 小于等于 l 的顶部元素
    if(l.isEmpty() || num <= l.peek()){
        l.offer(num);
        // 如果 l 的数量大于 r 的数量加 1
        if(l.size() > r.size() + 1){
            // 将 l 的顶部元素转移到 r
            r.offer(l.poll());
        }
    }else{
        // 如果 num 大于 l 的顶部元素
        r.offer(num);
        // 如果 r 的数量大于 l 的数量
        if(r.size() > l.size()){
            // 将 r 的顶部元素转移到 l
            l.offer(r.poll());
        }
    }
}

3. findMedian 方法

double findMedian() 返回到目前为止所有元素的中位数。这个方法直接利用了 l 和 r 的性质来找到中位数。

  • 如果 l 的数量大于 r 的数量,那么中位数就是 l 的顶部元素。
  • 如果 l 和 r 的数量相等,那么中位数就是 l 和 r 顶部元素的平均值。
public double findMedian() {
    // 如果 l 的数量大于 r 的数量
    if(l.size() > r.size()){
        // 中位数就是 l 的顶部元素
        return l.peek();
    }
    // 如果 l 和 r 的数量相等
    return (l.peek() + r.peek()) / 2.0;
}

这样,我们就完成了一个 MedianFinder 类的实现,它可以在数据流中快速找到中位数。

Code

class MedianFinder {
    PriorityQueue<Integer> l;
    PriorityQueue<Integer> r;
    public MedianFinder() {
        l = new PriorityQueue<Integer>((a, b) -> (b - a));
        r = new PriorityQueue<Integer>((a, b) -> (a - b));
    }
    
    public void addNum(int num) {
        if(l.isEmpty() || num <= l.peek()){
            l.offer(num);
            if(l.size() > r.size() + 1){
                r.offer(l.poll());
            }
        }else{
            r.offer(num);
            if(r.size() > l.size()){
                l.offer(r.poll());
            }
        }
    }
    
    public double findMedian() {
        if(l.size() > r.size()){
            return l.peek();
        }
        return (l.peek() + r.peek()) / 2.0;
    }
}

/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder obj = new MedianFinder();
 * obj.addNum(num);
 * double param_2 = obj.findMedian();
 */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值