LeetCode 295:数据流的中位数(Median Finder)—— Java 题解 ✅

LeetCode 295:数据流的中位数(Median Finder)------ Java 题解 ✅

🔗 题目链接

👉 https://leetcode.cn/problems/find-median-from-data-stream/


📖 内容概要

设计一个数据结构,支持:

  • 动态添加整数
  • 实时返回当前已添加数据的中位数

✅ 使用 双堆(大小顶堆)

✅ 插入 O(log n)

✅ 查询 O(1)

✅ 面试 & 系统设计高频题


💡 解题思路(核心)

一、核心思想:以空间换时间

始终维持左右两部分数据:

  • 左半部分 ≤ 中位数
  • 右半部分 ≥ 中位数

二、数据结构设计

类型 作用
left 大顶堆 保存较小的一半
right 小顶堆 保存较大的一半
复制代码
left (maxHeap)  |  right (minHeap)
   ≤ median     |     ≥ median

三、堆的平衡规则(非常关键)

始终保持:

复制代码
left.size() == right.size()
或
left.size() == right.size() + 1

🔄 addNum 流程详解

情况 1:当前两堆大小相等

复制代码
left.size() == right.size()

这时要保证left的大小比right1,我们肯定要往left中加一个元素;但同时要保证中位数位置的正确性,我们不知道待添加元素和right的堆顶元素谁更小,所以先将待添加元素放到right中,相当于在right中做了一个排序,再把right的最小值(堆顶)弹出放到left中。

策略:

  1. 新数先入 right
  2. right 的最小值移到 left

✅ 保证 left 永远多一个

java 复制代码
right.offer(num);
left.offer(right.poll());

情况 2:当前 left 多一个元素

复制代码
left.size() > right.size()

这时要保证left的大小和right大相等,我们要往right中加一个元素;但同时要保证中位数位置的正确性,我们不知道待添加元素和left的堆顶元素谁更大,所以先将待添加元素放到left,相当于在left中做了一个排序,再把left的最大值(堆顶)弹出放到right中。

策略:

  1. 新数先入 left
  2. left 的最大值移到 right
java 复制代码
left.offer(num);
right.offer(left.poll());

🎯 findMedian 规则

情况 中位数
奇数个元素 left.peek()
偶数个元素 (left + right) / 2.0

✅ 举例说明

依次添加:1, 2, 3, 4

操作 left (大顶堆) right (小顶堆) median
add 1 1 \[\] 1
add 2 1 2 1.5
add 3 2,1 3 2
add 4 2,1 3,4 2.5

✅ AC 代码(Java)

java 复制代码
class MedianFinder {

    private PriorityQueue<Integer> left;  // 大顶堆
    private PriorityQueue<Integer> right; // 小顶堆

    public MedianFinder() {
        left = new PriorityQueue<>((a, b) -> b - a);
        right = new PriorityQueue<>();
    }

    public void addNum(int num) {
        if (left.size() == right.size()) {
            right.offer(num);
            left.offer(right.poll());
        } else {
            left.offer(num);
            right.offer(left.poll());
        }
    }

    public double findMedian() {
        if (left.size() > right.size()) {
            return left.peek();
        }
        return (left.peek() + right.peek()) / 2.0;
    }
}

⏱️ 复杂度分析

操作 复杂度
addNum O(log n)
findMedian O(1)
空间复杂度 O(n)

✅ 总结一句话

用大顶堆存小的一半,用小顶堆存大的一半,始终保持左堆 ≥ 右堆,中位数就在堆顶。


📌 面试加分点(建议记住)

  • ✅ 为什么先插右堆再移左堆?
    👉 保证 left 永远是"较小的一半的最大值"
  • ✅ 为什么不用排序?
    👉 排序 O(n log n),双堆 O(log n)
  • ✅ 可扩展到:
    • 滑动窗口中位数
    • Top K 动态数据
相关推荐
何以解忧,唯有..11 分钟前
高并发下超卖问题解决方案:从数据库到分布式锁的完整实践
java
行者全栈架构师23 分钟前
Spring Boot 接入 MaxKey 单点登录:6 个内部系统,一次登录全通行
java·vue.js·后端
木白CPP41 分钟前
Linux DMA驱动详解(二)-----DMA的使用者
java·linux·运维
caoerzhong1 小时前
JeeWMS 开源仓库管理系统 GPL-3.0 合规指南:Java WMS 二次开发前必须弄清的授权边界
java·开发语言·开源·vue
Sam_Deep_Thinking1 小时前
单一职责原则:JAVA LocalDate的设计取舍
java·后端·程序员·单一职责原则
杨杨杨大侠1 小时前
Java 不再需要 JVM?一文看懂 GraalVM Native Image、Substrate VM 与 JDK
java·jvm·java ee
Sylvia33.1 小时前
火星数据体育API|一站式接入足球篮球电竞等18+项目实时数据
java·开发语言·python·websocket·游戏
李少兄1 小时前
解决 MySQL Lock wait timeout exceeded 报错
java
va学弟1 小时前
Java 语言特性:泛型(Generics)
java·泛型
CodeRecycle2 小时前
Python 自动配置 pip 支持库(通过 Windows Bat 脚本)
算法