返回

进阶算法难题:295. 数据流的中位数,一文掌握分治法精髓

后端

算法解题指南:295. 数据流的中位数

题目

给你一个包含若干整数的数组,请设计一个数据结构,支持以下两种操作:

  1. addNum(val) - 将 val 添加到数据结构中。
  2. findMedian() - 返回目前所有元素的中位数。

进阶

  1. 如果数据量过大,该数据结构将如何伸缩?
  2. 如果需要对数据流进行随机访问,该数据结构将如何应对?

分治法简介

分治法是一种经典的算法范式,其基本思想是将一个问题分解成多个规模较小的子问题,然后递归地求解这些子问题,最后将子问题的解合起来得到原问题的解。分治法具有时间复杂度低、易于理解和实现等优点,广泛应用于各种算法领域。

基于分治法的解题思路

对于本题,我们可以采用分治法的思想将其分解为两个子问题:

  1. 维护两个优先队列:
    • minHeap:存储数据流中较大的那一半元素,并以升序排列。
    • maxHeap:存储数据流中较小的那一半元素,并以降序排列。
  2. 将新元素依次添加到两个优先队列中:
    • 如果新元素大于 maxHeap 的最大值,则将其添加到 minHeap 中。
    • 否则,将其添加到 maxHeap 中。
  3. 调整两个优先队列的大小,以确保 minHeap 的元素个数不超过 maxHeap 的元素个数。
  4. 返回两个优先队列的顶部的平均值作为中位数。

代码实现

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)。

空间复杂度分析

  • minHeapmaxHeap 存储的数据流中的一半元素,因此空间复杂度为 O(n)。

进阶问题解答

1. 如果数据量过大,该数据结构将如何伸缩?

我们可以使用一种称为“分治法”的算法范式来解决这个问题。分治法将问题分解成多个规模较小的子问题,然后递归地求解这些子问题,最后将子问题的解合起来得到原问题的解。这种方法可以大大降低算法的时间复杂度。

2. 如果需要对数据流进行随机访问,该数据结构将如何应对?

我们可以使用一种称为“跳表”的随机访问数据结构。跳表是一种平衡二叉树,具有快速查找和删除元素的特性。跳表还支持随机访问,因此我们可以快速访问数据流中的任何元素。

结语

本文深入分析了 LeetCode 上的 295. 数据流的中位数 这一难题,详细讲解了基于分治法的解题思路和代码实现。同时,还提供了两个进阶问题及其解答,帮助读者进一步理解算法的应用。希望本文能对你有所启发,让你在算法学习的道路上不断进步。