返回
进阶算法难题:295. 数据流的中位数,一文掌握分治法精髓
后端
2023-11-14 11:20:47
算法解题指南:295. 数据流的中位数
题目
给你一个包含若干整数的数组,请设计一个数据结构,支持以下两种操作:
addNum(val)
- 将val
添加到数据结构中。findMedian()
- 返回目前所有元素的中位数。
进阶
- 如果数据量过大,该数据结构将如何伸缩?
- 如果需要对数据流进行随机访问,该数据结构将如何应对?
分治法简介
分治法是一种经典的算法范式,其基本思想是将一个问题分解成多个规模较小的子问题,然后递归地求解这些子问题,最后将子问题的解合起来得到原问题的解。分治法具有时间复杂度低、易于理解和实现等优点,广泛应用于各种算法领域。
基于分治法的解题思路
对于本题,我们可以采用分治法的思想将其分解为两个子问题:
- 维护两个优先队列:
minHeap
:存储数据流中较大的那一半元素,并以升序排列。maxHeap
:存储数据流中较小的那一半元素,并以降序排列。
- 将新元素依次添加到两个优先队列中:
- 如果新元素大于
maxHeap
的最大值,则将其添加到minHeap
中。 - 否则,将其添加到
maxHeap
中。
- 如果新元素大于
- 调整两个优先队列的大小,以确保
minHeap
的元素个数不超过maxHeap
的元素个数。 - 返回两个优先队列的顶部的平均值作为中位数。
代码实现
import heapq
class MedianFinder:
def __init__(self):
self.minHeap = [] # 存储较大的那一半元素
self.maxHeap = [] # 存储较小的那一半元素
def addNum(self, val):
if not self.minHeap or val >= -self.minHeap[0]:
heapq.heappush(self.minHeap, -val) # 将元素添加到minHeap
else:
heapq.heappush(self.maxHeap, val) # 将元素添加到maxHeap
# 调整两个优先队列的大小
if len(self.minHeap) > len(self.maxHeap) + 1:
heapq.heappush(self.maxHeap, -heapq.heappop(self.minHeap))
elif len(self.maxHeap) > len(self.minHeap):
heapq.heappush(self.minHeap, -heapq.heappop(self.maxHeap))
def findMedian(self):
if len(self.minHeap) == len(self.maxHeap):
return (-self.minHeap[0] + self.maxHeap[0]) / 2
else:
return -self.minHeap[0]
# 测试用例
medianFinder = MedianFinder()
medianFinder.addNum(1)
medianFinder.addNum(2)
print(medianFinder.findMedian()) # 输出:1.5
medianFinder.addNum(3)
print(medianFinder.findMedian()) # 输出:2.0
medianFinder.addNum(4)
print(medianFinder.findMedian()) # 输出:2.5
时间复杂度分析
addNum
操作的时间复杂度为 O(log n),其中 n 是数据流中的元素个数。findMedian
操作的时间复杂度为 O(1)。
空间复杂度分析
minHeap
和maxHeap
存储的数据流中的一半元素,因此空间复杂度为 O(n)。
进阶问题解答
1. 如果数据量过大,该数据结构将如何伸缩?
我们可以使用一种称为“分治法”的算法范式来解决这个问题。分治法将问题分解成多个规模较小的子问题,然后递归地求解这些子问题,最后将子问题的解合起来得到原问题的解。这种方法可以大大降低算法的时间复杂度。
2. 如果需要对数据流进行随机访问,该数据结构将如何应对?
我们可以使用一种称为“跳表”的随机访问数据结构。跳表是一种平衡二叉树,具有快速查找和删除元素的特性。跳表还支持随机访问,因此我们可以快速访问数据流中的任何元素。
结语
本文深入分析了 LeetCode 上的 295. 数据流的中位数 这一难题,详细讲解了基于分治法的解题思路和代码实现。同时,还提供了两个进阶问题及其解答,帮助读者进一步理解算法的应用。希望本文能对你有所启发,让你在算法学习的道路上不断进步。