LeetCode题练习与总结:数据流的中位数--295

237 篇文章 0 订阅
42 篇文章 0 订阅

一、题目描述

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

  • 例如 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

提示:

  • -10^5 <= num <= 10^5
  • 在调用 findMedian 之前,数据结构中至少有一个元素
  • 最多 5 * 10^4 次调用 addNum 和 findMedian

二、解题思路

为了解决这个问题,我们可以使用两个堆(优先队列)来维护数据流中的中位数。具体来说,我们使用一个大顶堆(最大堆)来存储较小的一半元素,以及一个小顶堆(最小堆)来存储较大的一半元素。这样做的目的是使得大顶堆的堆顶元素和小顶堆的堆顶元素分别代表当前所有元素中的较小和较大的中间值。

以下是具体的实现步骤:

  • 初始化两个堆:一个大顶堆(记为maxHeap)用于存储较小的一半元素,一个小顶堆(记为minHeap)用于存储较大的一半元素。

  • 当添加一个数字时:

    • 如果maxHeap为空或者数字小于等于maxHeap的堆顶元素,我们将数字添加到maxHeap中。
    • 否则,我们将数字添加到minHeap中。
    • 为了保持两个堆的大小平衡,如果maxHeap的大小比minHeap的大小大2,我们需要将maxHeap的堆顶元素移动到minHeap中;反之亦然。
  • 当查询中位数时:

    • 如果maxHeapminHeap的大小相同,中位数是两个堆顶元素的平均值。
    • 如果它们的大小不同,中位数是大小较大的那个堆的堆顶元素。

三、具体代码

import java.util.PriorityQueue;
import java.util.Collections;

public class MedianFinder {
    private PriorityQueue<Integer> maxHeap; // 大顶堆,存储较小的一半元素
    private PriorityQueue<Integer> minHeap; // 小顶堆,存储较大的一半元素

    public MedianFinder() {
        maxHeap = new PriorityQueue<>(Collections.reverseOrder()); // 创建大顶堆
        minHeap = new PriorityQueue<>(); // 创建小顶堆
    }
    
    public void addNum(int num) {
        if (maxHeap.isEmpty() || num <= maxHeap.peek()) {
            maxHeap.offer(num);
        } else {
            minHeap.offer(num);
        }
        
        // 平衡两个堆的大小
        if (maxHeap.size() > minHeap.size() + 1) {
            minHeap.offer(maxHeap.poll());
        } else if (minHeap.size() > maxHeap.size()) {
            maxHeap.offer(minHeap.poll());
        }
    }
    
    public double findMedian() {
        if (maxHeap.size() == minHeap.size()) {
            return (maxHeap.peek() + minHeap.peek()) / 2.0;
        } else {
            return maxHeap.peek();
        }
    }
}

// 使用示例
// MedianFinder medianFinder = new MedianFinder();
// medianFinder.addNum(1);
// medianFinder.addNum(2);
// System.out.println(medianFinder.findMedian()); // 输出 1.5
// medianFinder.addNum(3);
// System.out.println(medianFinder.findMedian()); // 输出 2.0

四、时间复杂度和空间复杂度

1. 时间复杂度
  • addNum(int num) 方法:

    • offer 方法将一个元素添加到优先队列中,其时间复杂度是 O(logN),其中 N 是优先队列中的元素数量。
    • 在 addNum 方法中,我们最多调用两次 offer 方法,一次是添加元素到 maxHeap 或 minHeap,另一次是在两个堆之间调整元素以达到平衡。因此,addNum 方法的时间复杂度是 O(logN)。
  • findMedian() 方法:

    • peek 方法获取优先队列的堆顶元素,其时间复杂度是 O(1)。
    • 在 findMedian 方法中,我们最多调用两次 peek 方法(当两个堆的大小相等时)。由于 peek 是常数时间操作,所以 findMedian 方法的时间复杂度是 O(1)。
2. 空间复杂度
  • 整体空间复杂度:

    • 两个优先队列 maxHeap 和 minHeap 用来存储所有输入的数字。在最坏的情况下,所有的数字都存储在这两个堆中,因此空间复杂度是 O(N),其中 N 是输入数字的总数。
  • addNum(int num) 方法:

    • 该方法没有使用额外的空间,除了存储输入数字所需的堆空间。因此,addNum 方法的空间复杂度是 O(1)。
  • findMedian() 方法:

    • 该方法没有使用额外的空间,只是访问了两个堆的堆顶元素。因此,findMedian 方法的空间复杂度是 O(1)。

五、总结知识点

  • 类定义(Class Definition):

    • 代码定义了一个名为 MedianFinder 的类,该类用于找到数据流中的中位数。
  • 成员变量(Member Variables):

    • 类中有两个成员变量 maxHeap 和 minHeap,它们都是 PriorityQueue 类型,分别用于存储较小的一半和较大的一半元素。
  • 构造函数(Constructor):

    • 类的构造函数 MedianFinder() 负责初始化两个优先队列。maxHeap 使用 Collections.reverseOrder() 来创建一个大顶堆,而 minHeap 默认是一个小顶堆。
  • 优先队列(Priority Queue):

    • PriorityQueue 是一个基于优先级堆的无界优先队列,它提供了 O(logN) 时间复杂度的插入和删除操作。
  • 堆操作(Heap Operations):

    • offer() 方法用于将元素插入到优先队列中。
    • peek() 方法用于获取队列的头部元素,但不从队列中删除它。
    • poll() 方法用于获取并移除队列的头部元素。
  • 条件语句(Conditional Statements):

    • if-else 语句用于在 addNum 方法中决定元素应该添加到哪个堆中,以及在两个堆之间进行平衡。
  • 逻辑运算符(Logical Operators):

    • || (逻辑或) 和 && (逻辑与) 运算符用于 addNum 方法中的条件判断。
  • 算术运算(Arithmetic Operations):

    • 在 findMedian 方法中,使用加法(+)和除法(/)来计算中位数。
  • 类型转换(Type Conversion):

    • 在计算中位数时,使用了一个整数除以 2.0 来确保结果是一个浮点数。

以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

一直学习永不止步

谢谢您的鼓励,我会再接再厉的!

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

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

打赏作者

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

抵扣说明:

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

余额充值