📅  最后修改于: 2023-12-03 15:10:21.892000             🧑  作者: Mango
给定一个大小为n
的整型数组nums
,以及一个大小为k
的整型滑动窗口,依次遍历nums
中的每个长度为k
的窗口,求出窗口中所有数字的中位数。
对于每个滑动窗口,我们可以暴力将其排序(时间复杂度为$O(k\log{k})$),然后取中间的数(如果窗口大小为奇数)或中间两个数的平均值(如果窗口大小为偶数)作为中位数。这个算法的时间复杂度为$O(nk\log{k})$,其中$n$为数组nums
的长度,$k$为窗口大小。
然而,这个算法非常耗时,无法通过本题。因此我们需要尝试更高效的方法。
我们从方法一中得到的启示是:如果能在$O(\log{k})$的时间内找到当前窗口中的最大数和最小数(即左右区间的中位数),那么就可以得到当前窗口的中位数。
基于这个想法,我们可以使用两个优先队列(堆),一个大根堆维护较小的一半数,一个小根堆维护较大的一半数,使得大根堆的堆顶元素始终小于等于小根堆的堆顶元素。
每当窗口向右移动一位,我们需要将当前窗口的最左边的数从堆中删除,并将最新加入的数插入堆中。这个操作需要保持两个堆的大小以及堆顶元素的约束条件。具体实现可以参考这里。
这个算法的时间复杂度为$O(n\log{k})$,其中$n$为数组nums
的长度,$k$为窗口大小。相比方法一,它有一个非常大的优势:每次向右移动窗口时,我们只需维护两个大小为$k/2$的堆,而不需要对整个滑动窗口重新排序。因此,这个算法在时间复杂度上比方法一要优秀得多。
以下是实现方法二的Python代码示例:
import heapq
class DualHeap:
def __init__(self, k: int):
self.small = []
self.large = []
self.delayed = {}
self.k = k
self.smallSize = 0
self.largeSize = 0
def prune(self, heap: List[int]):
while heap:
num = heap[0]
if num in self.delayed:
heapq.heappop(heap)
self.delayed[num] -= 1
if not self.delayed[num]:
del self.delayed[num]
else:
break
def makeBalance(self):
if self.smallSize > self.largeSize + 1:
self.large.append(-self.small[0])
heapq.heappop(self.small)
self.smallSize -= 1
self.largeSize += 1
self.prune(self.small)
elif self.smallSize < self.largeSize:
self.small.append(-self.large[0])
heapq.heappop(self.large)
self.smallSize += 1
self.largeSize -= 1
self.prune(self.large)
def insert(self, num: int):
if not self.small or num <= -self.small[0]:
self.small.append(-num)
self.smallSize += 1
else:
self.large.append(num)
self.largeSize += 1
self.makeBalance()
def erase(self, num: int):
self.delayed[num] = self.delayed.get(num, 0) + 1
if num <= -self.small[0]:
self.smallSize -= 1
if num == -self.small[0]:
self.prune(self.small)
else:
self.largeSize -= 1
if num == self.large[0]:
self.prune(self.large)
self.makeBalance()
def getMedian(self):
return -self.small[0] if self.k % 2 == 1 else (self.large[0] - self.small[0]) / 2
class Solution:
def medianSlidingWindow(self, nums: List[int], k: int) -> List[float]:
dh = DualHeap(k)
for num in nums[:k]:
dh.insert(num)
ans = [dh.getMedian()]
for i in range(k, len(nums)):
dh.insert(nums[i])
dh.erase(nums[i-k])
ans.append(dh.getMedian())
return ans
注意:上述代码中的DualHeap
类是一个用于实现方法二的优先队列(堆)。具体细节可以参考这里。