📅  最后修改于: 2023-12-03 15:26:11.414000             🧑  作者: Mango
给定一个数组nums,有一个大小为k的滑动窗口从数组的最左边滑到最右边。你只能看到在滑动窗口k内的数字。滑动窗口每次只向右移动一位。求出每一个滑动窗口的中位数。
先了解一下中位数的概念,中位数是一个有序数列中间的数,当数列长度为偶数时取中间两个数的平均值。
题目要求我们求滑动窗口中的中位数,最简单的思路是每次对滑动窗口内的数进行排序,然后求中位数。但是,这样的时间复杂度是O(nklogk),不太理想。
我们可以使用两个堆,一个小根堆,一个大根堆来求解中位数。堆的大小为滑动窗口的一半加1,大根堆的堆顶为当前的中位数。每次滑动窗口向右移动一位,就需要将堆中的元素进行调整,使得大根堆中的元素个数等于小根堆中的元素个数,或者比小根堆大1。当滑动窗口大小为奇数时,中位数为大根堆的堆顶,当滑动窗口大小为偶数时,中位数为大根堆堆顶与小根堆堆顶的平均值。
代码如下(Python):
import heapq
class Solution:
def medianSlidingWindow(self, nums: List[int], k: int) -> List[float]:
# 初始化大根堆和小根堆
max_heap = []
min_heap = []
heapq.heapify(max_heap) # 初始化大根堆
heapq.heapify(min_heap) # 初始化小根堆
# 将前k个数分别加入到大根堆和小根堆中
for i in range(k):
heapq.heappush(max_heap, -nums[i])
if len(max_heap) > len(min_heap) + 1: # 大根堆的元素个数比小根堆的元素个数多1
heapq.heappush(min_heap, -heapq.heappop(max_heap))
if min_heap and -max_heap[0] > min_heap[0]: # 大根堆的堆顶元素大于小根堆的堆顶元素
heapq.heappush(max_heap, -heapq.heappop(min_heap))
heapq.heappush(min_heap, -heapq.heappop(max_heap))
res = [float(-max_heap[0] if k % 2 != 0 else (-max_heap[0] + min_heap[0]) / 2)]
for i in range(k, len(nums)):
if nums[i] <= -max_heap[0]: # 新添加的元素比大根堆的堆顶元素小,加入到大根堆中
heapq.heappush(max_heap, -nums[i])
if len(max_heap) > len(min_heap) + 1:
heapq.heappush(min_heap, -heapq.heappop(max_heap))
else: # 新添加的元素比大根堆的堆顶元素大,加入到小根堆中
heapq.heappush(min_heap, nums[i])
if len(min_heap) > len(max_heap):
heapq.heappush(max_heap, -heapq.heappop(min_heap))
# 维护大根堆和小根堆的元素个数
if len(max_heap) > len(min_heap) + 1:
heapq.heappush(min_heap, -heapq.heappop(max_heap))
elif len(min_heap) > len(max_heap):
heapq.heappush(max_heap, -heapq.heappop(min_heap))
res.append(float(-max_heap[0] if k % 2 != 0 else (-max_heap[0] + min_heap[0]) / 2))
return res
使用两个堆来维护中位数,每次堆的插入和弹出操作的时间复杂度为O(logk),需要插入n个元素,因此总时间复杂度为O(nlogk)。
需要使用两个堆来维护中位数,因此空间复杂度为O(k)。
本题目中,我们使用了两个堆来维护中位数,通过这种方式,可以将时间复杂度降低到O(nlogk)。需要注意的是,我们需要维护两个堆的元素个数,使得大根堆的元素个数等于小根堆的元素个数或比小根堆的元素个数大1。