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 动态数据
相关推荐
凤凰院凶涛QAQ12 小时前
《Java版数据结构 & 集合类剖析》栈与队列:“push/pop 是栈的灵魂,offer/poll 是队列的骨架——四组 API,两种人生”
java·开发语言·数据结构
科技大视界12 小时前
投资AI项目,传统尽调不够用了——李章虎律师拆解算法、数据、算力三大雷区
人工智能·算法·数据挖掘
郝学胜-神的一滴12 小时前
算法实战:最小k个数——大顶堆的优雅解法
开发语言·数据结构·c++·python·程序人生·算法·排序算法
Irissgwe12 小时前
算法滑动窗口
数据结构·算法
掘金_答案13 小时前
上线那天,一个 ConcurrentHashMap 差点送走我的 AI 客服——3 天排查 JVM 血泪史
java·后端·架构
怪兽学LLM13 小时前
LeetCode 105. 从前序与中序遍历序列构造二叉树:分治递归思路详解
算法·leetcode·职场和发展
可编程芯片开发13 小时前
基于PI控制的三相整流器控制系统的simulink建模与仿真,包含超级电容充电和电机
算法
酿情师13 小时前
区块链共识算法深度拆解:PoW、PoS、PBFT、Raft 原理解析
算法·区块链·共识算法
MindUp13 小时前
企业网盘权限模型解析:多层级访问控制与审计能力选型指南
java·linux·运维
hansang_IR13 小时前
【记录】「SCOI2016」三道模拟赛/26.7.12
c++·算法