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的大小比right大1,我们肯定要往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 动态数据
相关推荐
Code_Solitude15 分钟前
c语言:二分法的完整补充
c语言·开发语言·算法
一切皆是因缘际会16 分钟前
掌控信息论:同源星际通信基础理论
人工智能·算法·ai
vx-程序开发1 小时前
【计算机毕设】51300童装超市管理系统的设计与实现
java·vue.js·spring boot·源码·课程设计·毕设·大作业
ym hyd 1111 小时前
题库建立与考试系统源码 Java+SpringBoot+Vue3 前后分离
java·开发语言·vue.js·spring boot·毕设
risc1234561 小时前
什么是码点?
java
JavaPub-rodert2 小时前
公共 Docker 镜像源总失效?用 Harbor Proxy Cache 自建 Docker Hub 镜像缓存
java·缓存·docker
智鸟科技GemeOpen开发者智能设备2 小时前
平安校园 AI 智能实时告警系统 智鸟科技·GemeOpen + 谷华科技·融合创新方案白皮书
java·开发语言·python·物联网·智能家居
自强的小白2 小时前
nacos配置中心面试题(导入配置的优先级),注意是如果后导入相同配置是以 后导入优先。外部入优先和nacos的数据隔离。
java·微服务
Wang's Blog2 小时前
Java框架 SpringCloud 快速入门: Feign 的自定义配置与日志级别
java·开发语言·spring cloud
成旭先生2 小时前
企业全景信息查询 API:工商照面 33 项、股东出资、变更与社保一次查全
java·开发语言·api接口·企业信息查询·工商数据·风控尽调·供应商准入