可视化图解算法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 大核心算法模块全解析:

✅ 链表

✅ 二叉树

✅ 二分查找、排序

✅ 堆、栈、队列

✅ 回溯算法

✅ 哈希算法

✅ 动态规划

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

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

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

相关推荐
怀旧,9 分钟前
【C++】26. 智能指针
开发语言·c++·算法
dragoooon3411 分钟前
[优选算法专题三.二分查找——NO.24搜索旋转排序数组中的最⼩值]
数据结构·leetcode·动态规划
Haooog12 分钟前
654.最大二叉树(二叉树算法)
java·数据结构·算法·leetcode·二叉树
Swift社区18 分钟前
LeetCode 392 判断子序列
算法·leetcode·职场和发展
芒果量化35 分钟前
ML4T - 第7章第8节 利用LR预测股票价格走势Predicting stock price moves with Logistic Regression
算法·机器学习·线性回归
东方芷兰1 小时前
JavaWeb 课堂笔记 —— 20 SpringBootWeb案例 配置文件
java·开发语言·笔记·算法·log4j·intellij-idea·lua
Diligence8152 小时前
最优化方法
算法
会编程是什么感觉...2 小时前
算法 - FOC闭环位置控制
算法·foc
那我掉的头发算什么2 小时前
【数据结构】双向链表
java·开发语言·数据结构·链表·intellij-idea·idea
半桔2 小时前
【STL源码剖析】从源码看 list:从迭代器到算法
java·数据结构·c++·算法·stl·list