数据结构与算法之 leetcode 队列滑动窗口中位数 双堆实现

数据结构与算法阿木 发布于 2025-07-12 6 次阅读


摘要:

在LeetCode中,有一个经典的问题叫做“队列滑动窗口中位数”。这个问题要求我们使用队列结构来维护一个滑动窗口,并实时计算出窗口中的中位数。本文将深入探讨这个问题,并使用双堆(两个优先队列)的方法来实现这一功能。

一、问题背景

中位数是一组数据排序后位于中间位置的数。在滑动窗口问题中,我们需要维护一个窗口,并随着窗口的滑动,实时计算出窗口中的中位数。这个问题在数据流处理、实时监控等领域有着广泛的应用。

二、解决方案

为了实现队列滑动窗口中位数,我们可以使用两个优先队列(最大堆和最小堆)来维护窗口中的数据。以下是具体的实现步骤:

1. 创建两个优先队列:一个最大堆(maxHeap)和一个最小堆(minHeap)。

2. 当窗口中的元素进入时,将其插入到最小堆中。

3. 如果最小堆中的元素个数大于最大堆中的元素个数,则将最小堆的堆顶元素(即最小值)插入到最大堆中。

4. 如果最大堆中的元素个数大于最小堆中的元素个数,则将最大堆的堆顶元素(即最大值)插入到最小堆中。

5. 当窗口中的元素离开时,如果离开的元素在最小堆中,则将其从最小堆中移除;如果离开的元素在最大堆中,则将其从最大堆中移除。

6. 每次窗口滑动后,根据最大堆和最小堆中的元素个数,计算出窗口中的中位数。

三、代码实现

以下是使用Java语言实现的代码示例:

java

import java.util.PriorityQueue;

public class MedianFinder {


private PriorityQueue<Integer> maxHeap; // 最大堆


private PriorityQueue<Integer> minHeap; // 最小堆

public MedianFinder() {


maxHeap = new PriorityQueue<>((a, b) -> b - a); // 降序排列


minHeap = new PriorityQueue<>(); // 升序排列


}

public void addNum(int num) {


minHeap.offer(num); // 先将元素插入最小堆


maxHeap.offer(minHeap.poll()); // 将最小堆的堆顶元素插入最大堆


if (maxHeap.size() < minHeap.size()) {


minHeap.offer(maxHeap.poll()); // 如果最大堆元素个数小于最小堆,则将最大堆的堆顶元素插入最小堆


}


}

public double findMedian() {


if (maxHeap.size() == minHeap.size()) {


return (maxHeap.peek() + minHeap.peek()) / 2.0; // 如果两个堆元素个数相等,则中位数为两个堆的堆顶元素的平均值


} else {


return maxHeap.peek(); // 如果最大堆元素个数大于最小堆,则中位数为最大堆的堆顶元素


}


}


}


四、性能分析

使用双堆实现队列滑动窗口中位数的方法具有以下优点:

1. 时间复杂度:每次插入和删除操作的时间复杂度均为O(logn),其中n为窗口中元素的数量。

2. 空间复杂度:需要维护两个优先队列,空间复杂度为O(n)。

五、总结

本文深入解析了LeetCode问题“队列滑动窗口中位数”,并使用双堆(两个优先队列)的方法实现了这一功能。通过分析问题背景、解决方案和代码实现,我们了解到使用双堆可以有效地维护滑动窗口中的中位数。在实际应用中,这种方法可以应用于数据流处理、实时监控等领域,具有广泛的应用前景。