可视化图解算法52:数据流中的中位数

牛客网 面试笔试 TOP101 | LeetCode 295. 数据流的中位数

1. 题目

描述

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。

数据范围:数据流中数个数满足 1 ≤n ≤1000 ,大小满足 1≤val≤1000

进阶: 空间复杂度 O(n), 时间复杂度 O(nlogn)

示例1

输入:

5,2,3,4,1,6,7,0,8

返回值:

"5.00 3.50 3.00 3.50 3.00 3.50 4.00 3.50 4.00 "

说明:

数据流里面不断吐出的是5,2,3...,则得到的平均数分别为5,(5+2)/2,3...

示例2

输入:

1,1,1

返回值:

"1.00 1.00 1.00 "

2. 解题思路

本题的核心是要明确数据流中的中位数的含义:

数据流中的中位数是指,在数据持续动态流入(即数据流)的过程中,实时计算并维护当前已接收数据的中位数。由于数据是连续到达的,无法一次性获取全部数据,因此需要设计高效的算法和数据结构来支持动态插入和快速查询中位数的操作。

关键点解释

  1. 中位数的定义

    • 若数据个数为奇数,中位数是排序后中间位置的数;
    • 若数据个数为偶数,中位数是中间两个数的平均值。
  2. 数据流的特性

    • 数据是动态到达的,无法预知总数据量。

    • 需支持两种操作:

      • 插入操作:将新数据添加到现有数据中;
      • 查询操作:实时获取当前所有数据的中位数。
  3. 核心挑战

    • 直接对动态数据排序的效率低下(时间复杂度为 O(n log n))。
    • 需设计一种数据结构,使得插入和查询的时间复杂度尽可能低(如 O(log n) 和 O(1))。

常用解决方案

通常使用双堆(最大堆+最小堆) 来高效维护数据流的中位数:

  • 最大堆(Max Heap) :存储较小的一半数据,堆顶是这半部分的最大值。
  • 最小堆(Min Heap) :存储较大的一半数据,堆顶是这半部分的最小值。
  • 平衡条件:两堆的大小之差不超过 1。

插入操作

  1. 新数据插入其中一个堆,保持平衡条件;
  2. 若堆顶元素不满足大小关系,交换堆顶元素;
  3. 调整堆的大小,确保两堆大小差 ≤ 1。

查询中位数

  • 若两堆大小相等,中位数是两堆顶的平均值;
  • 若不等,中位数是较大堆的堆顶。

具体思路如下:

如果文字描述的不太清楚,你可以参考视频的详细讲解。

3. 编码实现

核心代码如下:

go 复制代码
var (
  //1.定义大顶堆和小顶堆
  maxHeap = make(MaxHep, 0) //大顶堆
  minHeap = make(MinHep, 0) //小顶堆
  count   = 0               //记录当前添加到堆中(包括大顶堆和小顶堆)的数据个数
)
​
func Insert(num int) {
  //2. 添加数据
  //2.1 个数为偶数的话,则先插入到大顶堆,并调整(弹出大顶堆中最大的数插入小顶堆中)
  if count%2 == 0 {
    heap.Push(&maxHeap, num)
    v := heap.Pop(&maxHeap)
    heap.Push(&minHeap, v)
  } else {
    //2.2 个数为奇数的话,则先插入到小顶堆,并调整(弹出小顶堆中最小的数插入大顶堆中)
    heap.Push(&minHeap, num)
    v := heap.Pop(&minHeap)
    heap.Push(&maxHeap, v)
  }
  count++
}
​
func GetMedian() float64 {
  // 3. 中位数的计算
  // 3.1 当前元素为偶数个,则取小顶堆和大顶堆的堆顶元素求平均
  if count%2 == 0 {
    return float64(maxHeap[0]+minHeap[0]) / 2
  }
  //3.2 当前元素为奇数个,则直接从小顶堆中取元素即可
  return float64(minHeap[0])
}
​

具体完整代码你可以参考下面视频的详细讲解。

4.小结

数据流中的中位数可以通过堆来实现,具体操作步骤为:

  1. 定义一个小顶堆和一个大顶堆,大小都为 n(题目要求:数据流中数的个数满足 1≤n≤1000));

  2. 大顶堆存储 n/2 个最小的数,小顶堆存储 n/2 个最大的数(小顶堆中的数据都大于大顶堆中的数据);

    • 如果堆(大顶堆和小顶堆)中的数据个数为偶数,则取小顶堆和大顶堆的堆顶元素求平均;
    • 如果堆(大顶堆和小顶堆)中的数据个数为奇数,则直接从小顶堆中取元素即可。

《数据结构与算法》深度精讲课程正式上线啦!7 大核心算法模块全解析:

✅ 链表

✅ 二叉树

✅ 二分查找、排序

✅ 堆、栈、队列

✅ 回溯算法

✅ 哈希算法

✅ 动态规划

无论你是备战笔试面试 、提升代码效率 ,还是突破技术瓶颈,这套课程都将为你构建扎实的算法思维底座。🔥立即加入学习打卡,与千名开发者共同进阶!

对于数据结构与算法,我们总结了一套【可视化+图解】方法,依据此方法来解决相关问题,算法变得易于理解,写出来的代码可读性高也不容易出错。具体也可以参考视频详细讲解。

今日佳句:独自莫凭栏,无限江山,别时容易见时难。

相关推荐
天机️灵韵14 分钟前
谷歌时间序列算法:零样本预测如何重塑行业决策?
人工智能·python·算法·开源项目
飞火流云1 小时前
LSM树与B+树优缺点分析
数据结构·b树
代码的余温1 小时前
B树与B+树:数据库索引背后的秘密
数据结构·数据库·b树
zmuy2 小时前
148. 排序链表
数据结构·链表
W说编程2 小时前
算法导论第十四章 B树与B+树:海量数据的守护者
c语言·数据结构·b树·算法·性能优化
Jay Kay2 小时前
深入解析协程:高并发编程的轻量级解决方案
开发语言·c++·算法
岁忧2 小时前
(LeetCode 每日一题) 2966. 划分数组并满足最大差限制 (贪心、排序)
java·c++·算法·leetcode·职场和发展·go
lifallen2 小时前
Java BitSet类解析:高效位向量实现
java·开发语言·后端·算法
学不好python的小猫2 小时前
7-4 身份证号处理
开发语言·python·算法