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 动态数据
相关推荐
凯酱20 分钟前
MyBatis 中 OGNL 的使用:从入门到实战
java·tomcat·mybatis
ting94520006 小时前
Humalike X Hermes 深度技术剖析:单指令注入群聊社交智能的底层架构、算法与跨 IM 平台实现
人工智能·算法·架构
吴声子夜歌6 小时前
Java面试——算法
java·算法·面试
evans在进步6 小时前
LeetCode 64:最小路径和——Java 原地动态规划详解
java·leetcode·动态规划
h_a_o777oah7 小时前
【图论】Tarjan 缩点:解决有向图中环的问题
c++·算法·图论·acm·强连通分量·缩点·tarjan
Tisfy7 小时前
LeetCode 1386.安排电影院座位:哈希表+位运算
算法·leetcode·散列表·题解·哈希表
Nil2087 小时前
leetcode 199二叉树的右视图
算法·leetcode·深度优先
凤山老林8 小时前
Spring Boot 3.x AOT 编译实战:从原理、踩坑到生产落地
java·spring boot·后端
博傅8 小时前
Spring 核心原理
java·后端·spring
yurenpai(27届找实习中)8 小时前
从零读懂 AI 智能客服(一):模块职责与 SSE 聊天链路(后端架构)
java·人工智能·架构·langchain4j