【LeetCode笔记】剑指Offer 41. 数据流中的中位数(Java、堆、优先队列、知识点)

打卡第十一天~

题目描述

  • 虽然但是,这是一道很nice的题目(涉及的知识点、运用很实用,见知识点模块)
    在这里插入图片描述
    在这里插入图片描述

知识点

1. 优先队列
  • priorityQueue 是 Queue 接口的实现,可以对其中元素进行排序
  • 默认顺序是升序;采用的是堆排序(小顶堆),因此只能保证根是最值,整个堆并不是有序的。
  • 自定义比较类:在 priorityQueue 的构造函数参数中用 Lambda 表达式表示即可。
2. Java 中 queue 的 offer、poll 等区别
  • add、offer:都是添加;队满情况 add 抛出异常,而 offer 则返回 false,可用于判断
  • remove、poll:都是删除首元素;队空情况remove抛出异常,而 poll 返回 null
  • element、peek:都是查询首元素;队空情况element抛出异常,而peek 返回 null

思路 && 代码

  • 代码量不大,主要考察的思路~
  • 首先中位数需要考虑总数奇偶情况:奇取中值,偶取平均值。
  • 然后既然想获得好的时间复杂度,那就空间换时间了(否则数组慢慢来也行了)
  • 接着,既然中位数是在排序后元素集的中间,那么我们可以有这么一个考虑:中间靠两个数据结构实现(毕竟一半一半嘛!),而有序(或局部有序)的数据结构中,进行 add 操作的时间复杂度最低的(O(logn))就是堆了!
  • 因此,我们选取优先队列来作为实现的数据结构~
  • 主要思路大顶堆存储较小部分,小顶堆存储较大部分。通过两个堆的堆顶来取中位数。通过先添加到堆a,再添加堆a的堆顶到堆b的“洗元素”方式来维护较大较小的属性。
class MedianFinder {
    Queue<Integer> smallHeap, bigHeap;
    /** initialize your data structure here. */
    public MedianFinder() {
        // 各存一半:small 小顶堆存储较大部分;big 大顶堆存储较小部分
        smallHeap = new PriorityQueue<>();
        bigHeap = new PriorityQueue<>((x, y) -> (y - x));
    }
    
    // 维护两个 Heap 的较大、较小属性:O(logn)
    public void addNum(int num) {
        // N = 奇数:增加 bigHeap 元素,通过先添加到 smallHeap,再获取其堆顶实现
        if(smallHeap.size() != bigHeap.size()) {
            smallHeap.add(num);
            bigHeap.add(smallHeap.poll());
        } 
        // N = 偶数:增加 small 元素,通过先添加到 bigHeap,再获取其堆顶实现
        else {
            bigHeap.add(num);
            smallHeap.add(bigHeap.poll());
        }
    }
    
    // O(1)
    public double findMedian() {
        // 偶数情况
        if(smallHeap.size() == bigHeap.size()) {
            // 较大堆的最小值 + 较小堆里的最大值
            return (smallHeap.peek() + bigHeap.peek()) / 2.0;
        }
        // 奇数情况
        else {
            return smallHeap.peek();
        }
    }
}

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

二刷

  • 关键词:优先队列、堆之间洗数据、Lambda 表达式
  • 思路还是难忘,代码也不多。这道题保持 hard 的时间估计不长了…
class MedianFinder {
    Queue<Integer> smallTop = new PriorityQueue<>();
    Queue<Integer> bigTop = new PriorityQueue<>((x, y) -> (y - x));

    public MedianFinder() {}
    
    public void addNum(int num) {
        if(smallTop.size() == bigTop.size()) {
            bigTop.add(num);
            smallTop.add(bigTop.poll());
        }
        else {
            smallTop.add(num);
            bigTop.add(smallTop.poll());
        }
    }
    
    public double findMedian() {
        if(smallTop.size() == bigTop.size()) {
            return (smallTop.peek() + bigTop.peek()) / 2.0;
        }
        else {
            return smallTop.peek();
        }
    }
}

版权声明:本文为qq_45108415原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。