发布时间:2026/9/5 22:01:25
CS-Notes 剑指 Offer 41.1 数据流中的中位数:用两个堆在线维护中位数 CS-Notes 剑指 Offer 41.1 数据流中的中位数用两个堆在线维护中位数【免费下载链接】CS-Notes:books: 技术面试必备基础知识、Leetcode、计算机操作系统、计算机网络、系统设计项目地址: https://gitcode.com/GitHub_Trending/cs/CS-Notes本文基于 CS-Notes 仓库中的题解文档 41.1 数据流中的中位数讲解如何在元素逐个到达、无法一次性排序的数据流场景下用大顶堆 小顶堆的对偶结构把中位数查询做到 O(1)、插入做到 O(log N)。读完本篇你将掌握该题完整的 Java 实现、两个堆必须维持的不变式证明思路以及它与仓库中同系列的最小的 K 个数共用的大顶堆技巧。题目描述本题出自《剑指 Offer》第 41 题的第一问属于仓库剑指 Offer 题解 - 目录中栈队列堆分类下的典型题目原题在线练习平台为牛客网。如何得到一个数据流中的中位数如果从数据流中读出奇数个数值那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值那么中位数就是所有数值排序之后中间两个数的平均值。题目给出了两个操作接口Insert(Integer val)数据流来一个新元素需要插入GetMedian()随时查询当前已读入全部元素的中位数。难点在于**流式 任意时刻可查询**元素是陆续到达的任何时刻都可能被要求给出中位数因此不能来一批再排序必须用某种数据结构在插入时就维护好中位数所需的信息。解题思路大顶堆存左半边小顶堆存右半边朴素做法是维护一个有序数组每次插入 O(N)、查询 O(1)或者每次查询时现排插入 O(1)、查询 O(N log N)。两种方案都偏科无法同时满足插入快、查询也快。官方题解采用的核心思想是用两个堆把数据流对半切让中位数永远落在两个堆顶的交汇处成员变量类型职责leftPriorityQueueInteger比较器(o1, o2) - o2 - o1即大顶堆存储排序后左半边较小的一半元素堆顶是左半边的最大值rightPriorityQueueInteger默认比较器即小顶堆存储排序后右半边较大的一半元素堆顶是右半边的最小值Nint当前数据流已读入的元素个数这里涉及的两个堆正是仓库 Java 容器 中介绍的 Queue 分类下的PriorityQueue——基于堆结构实现可以用它来实现优先队列默认是小顶堆初始化时传入比较器即可反转为大顶堆。两个必须维持的不变式整个算法正确性依赖以下两条不变式invariant有序性不变式right中的每个元素都大于等于left中的每个元素。这样两个堆顶就是整个数据集排序后正中间的 1 个或 2 个元素规模平衡不变式两堆大小相差不超过 1且奇偶关系由N决定——N为奇数时right比left多一个元素N为偶数时两堆等大。维持住这两条不变式后中位数查询就是 O(1)N为奇数中位数是排序后的中间一个数恰好是right的堆顶右半边的最小值N为偶数中位数是中间两数的平均值恰好是left.peek() right.peek()的平均值。Insert先插入错误的一边再把堆顶挪到另一边Insert的写法看似绕其实是在利用堆顶特性以 O(log N) 完成跨堆搬运从而保证有序性不变式/* 大顶堆存储左半边元素 */ private PriorityQueueInteger left new PriorityQueue((o1, o2) - o2 - o1); /* 小顶堆存储右半边元素并且右半边元素都大于左半边 */ private PriorityQueueInteger right new PriorityQueue(); /* 当前数据流读入的元素个数 */ private int N 0; public void Insert(Integer val) { /* 插入要保证两个堆存于平衡状态 */ if (N % 2 0) { /* N 为偶数的情况下插入到右半边。 * 因为右半边元素都要大于左半边但是新插入的元素不一定比左半边元素来的大 * 因此需要先将元素插入左半边然后利用左半边为大顶堆的特点 * 取出堆顶元素即为最大元素此时插入右半边 */ left.add(val); right.add(left.poll()); } else { right.add(val); left.add(right.poll()); } N; }逐分支解释这是原文档给出的完整实现逐行注释如下N 为偶数此时两堆等大插入后应由right变多一个目标边是right但新元素val完全可能比left里某些元素还小不能直接扔进right。于是先把val塞进left大顶堆此时left的堆顶就是左半边 val中的最大值把它poll出来放进right。这个最大值正是排序后应该属于右半边的最小那个数一次add 一次poll就同时完成了入堆与修正归属N 为奇数此时right多一个元素插入后应由left变多一个对称操作——先right.add(val)再把right堆顶右半边最小值搬进left。每轮插入都恰好发生一次跨堆搬运所以规模平衡不变式天然成立无需额外的 rebalance 逻辑。GetMedian只读堆顶O(1) 返回public Double GetMedian() { if (N % 2 0) return (left.peek() right.peek()) / 2.0; else return (double) right.peek(); }N为偶数两堆等大中间两个数就是left的最大值与right的最小值。注意除以2.0触发浮点除法避免整数截断N为奇数中间那个数就是right的堆顶强转double后返回。两个方法都只用到peekO(1)配合Insert的两次堆操作得到整体复杂度操作时间复杂度说明InsertO(log N)一次add 一次poll均为 O(log N)GetMedianO(1)仅读堆顶空间O(N)N 个元素全部分布在两个堆中对比数组排序方案查询 O(1) 但插入 O(N)或查询 O(N)双堆方案把两个操作都压到对数以内这正是它适合数据流场景的原因。执行过程走查以插入序列 5、2、3、4 为例按上面的代码逐步跟踪可以直观看到不变式如何被维持步骤操作left大顶堆堆顶在前right小顶堆堆顶在前N 后GetMedian验证全量排序1Insert(5)[5] → 搬出 5[5]15.0[5] → 5 ✓2Insert(2)[2]2 先入 left再与 5 比较后留下[5]2(25)/2 3.5[2,5] → 3.5 ✓3Insert(3)[3, 2]3 先入 left3 出堆顶[3, 5]33.0[2,3,5] → 3 ✓4Insert(4)[3, 2]4 先入 right3 出堆顶[4, 5]4(34)/2 3.5[2,3,4,5] → 3.5 ✓注意第 2 步val 2小于左半边已有的 5它先入left后堆顶仍是 55 被搬到right2 留在left——这正是新元素不一定比左半边大时该分支存在的意义。第 4 步则相反val 4进入right后堆顶是最小的 33 被搬回left。无论哪种情况搬动的总是当前跨界的那个元素。细节与易错点比较器溢出隐患仓库题解与 40. 最小的 K 个数 一致用(o1, o2) - o2 - o1实现大顶堆。做减法在极端值下可能溢出例如o1 Integer.MIN_VALUE更稳妥的等价写法是(o1, o2) - Integer.compare(o2, o1)面试中说明这一点是加分项。偶数个数时除的是 2.0(left.peek() right.peek()) / 2.0若写成/ 2会做整数除法截断小数。堆的归属只约束跨界元素本方案从不移动堆内普通元素每次插入只做一次堆顶搬运这是 O(log N) 复杂度的来源也是它与两个有序列表手动 rebalance方案的本质区别。空流与查询时机接口约定GetMedian在已插入元素后调用N计数即用来判断当前属于奇数还是偶数情形无需额外调用size()。为什么不用别的方案数组 每次排序查询快但插入 O(N)数据流持续增长时总代价劣于堆平衡二叉搜索树如 TreeSetJava 的TreeSet基于红黑树见 Java 容器 中 Set 分类也能做到插入/查询 O(log N)但要取第 N/2 个位置仍需额外维护索引或顺序统计双堆方案更简单且只关心中间一个/两个位置堆顶恰好就是答案是最贴合题意的结构快速选择QuickSelect单次求中位数 O(N)但无法在每次插入后低成本复用不适合任意时刻查询的流式场景。仓库中快速选择的思想可见 40. 最小的 K 个数 的第二个方案适合一次性离线求解与本在线场景正好互补。仓库中的相关题解最小的 K 个数与本题共用大顶堆 堆顶即当前极值的技巧该题解还专门强调应该使用大顶堆来维护最小堆以及PriorityQueue比较器的用法41.2 字符流中第一个不重复的字符同属流式插入 即时查询题型用频次数组 队列维护答案可与双堆方案对照体会流式问题的通用解法思路剑指 Offer 题解 - 目录本题归类于栈队列堆章节可顺藤摸瓜练习同分类的9. 用两个栈实现队列、59. 滑动窗口的最大值等。小结数据流中位数问题的关键是把中位数转化为两个堆顶left大顶堆存较小一半、right小顶堆存较大一半插入时通过先入目标侧的对面、再搬堆顶的一次跨堆操作同时完成入堆与归属修正从而在 O(log N) 插入、O(1) 查询下任意时刻返回正确中位数。完整可运行代码见仓库题解 41.1 数据流中的中位数配合上文走查表验证每一步后可以直接在刷题平台上实现Insert与GetMedian两个方法。【免费下载链接】CS-Notes:books: 技术面试必备基础知识、Leetcode、计算机操作系统、计算机网络、系统设计项目地址: https://gitcode.com/GitHub_Trending/cs/CS-Notes创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

2026/9/5 22:01:25

PandasAI:用自然语言搞定CSV与数据库数据分析,新手向

PandasAI:用自然语言搞定CSV与数据库数据分析,新手向 【免费下载链接】pandas-ai Chat with your database or your datalake (SQL, CSV, parquet). PandasAI makes data analysis conversational using LLMs and RAG. 项目地址: https://gitcode.com/…

2026/9/5 22:51:30

崩坏星穹铁道版本资源追踪:货币战争与零和博弈的Python分析

各位关注《崩坏:星穹铁道》版本动态的朋友,大家好。最近在整理版本更新资料时,看到一条类似“《崩坏:星穹铁道》货币战争:零和博弈2月19日A8N17升A8N18动能激发剑丹恒饮月”的标题信息。这类标题通常把版本事件、活动关…

2026/9/5 22:51:30

Apktool 安装部署:从零搭建到验证

Apktool 安装部署:从零搭建到验证 【免费下载链接】Apktool A tool for reverse engineering Android apk files 项目地址: https://gitcode.com/GitHub_Trending/ap/Apktool Apktool 是一款面向 Android 逆向工程师的命令行工具,把闭源 APK 的资…

2026/9/5 22:51:30

原生PHP校园失物招领系统:PDO封装+GPS定位+多图上传实战

简介:本资源是一套完整的基于PHP与MySQL开发的校园失物招领系统源码,面向Web开发初学者、高校计算机专业学生及课程设计实践者,旨在解决校园场景下失物信息分散、匹配低效、管理缺位等实际问题。压缩包为ZIP格式,大小27.28MB&…

2026/9/5 2:46:54

vSound小提琴数字处理器实操指南:从接线到演出的完整配置

电小提琴或者原声小提琴插电演出,第一个绕不开的坎就是声音难听。原声琴的共鸣和空气感一旦进了拾音器,出来的往往是一坨干瘪、发尖、带着奇怪塑料味的信号。我当初第一次把琴接上乐队调音台,直接被主唱吐槽"你这声音像在锯钢丝"。…

2026/9/5 2:46:52

传感器接口IC如何攻克生物化学传感的微弱信号难题?

1. 从电极到比特流:为什么生物化学传感必须依赖专用接口IC 做生物化学传感的人都有过类似的经历:明明传感器本身性能很好,信号输出却一塌糊涂——噪声大、漂移明显、重复性差,怎么调都达不到预期。很多时候问题并不在传感器&#…

2026/9/5 2:44:34

STM32F411CEU6多通道ADC采集:扫描模式+DMA实现详解

1. 多通道 ADC 的用武之地把“Multichannel ADC”和“STM32F411CEU6”这两个关键字放在一起,其实就是嵌入式开发里最常遇到的一类需求:用一块不算贵的 MCU,同时采集多路模拟信号。STM32F411CEU6 是 48 引脚的 Cortex-M4F 主控,主频…

2026/9/5 0:04:47

流式背压机制:避免前端渲染卡死与内存暴涨的滑动窗口限流

流式背压机制:避免前端渲染卡死与内存暴涨的滑动窗口限流在大模型流式输出(Streaming)与智能体实时推流的架构中,生产环境中经常出现一种“上下游生产消费速率严重失衡”的极端情况: 生产端极速产出:大模型…

2026/9/5 2:45:13

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/5 2:30:42

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/5 2:46:50

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…