class Solution {
public:
vector<double> medianSlidingWindow(vector<int>& nums, int k) {
}
};
480. 滑动窗口中位数
中位数是有序序列最中间的那个数。如果序列的长度是偶数,则没有最中间的数;此时中位数是最中间的两个数的平均数。
例如:
[2,3,4]
,中位数是 3
[2,3]
,中位数是 (2 + 3) / 2 = 2.5
给你一个数组 nums,有一个长度为 k 的窗口从最左端滑动到最右端。窗口中有 k 个数,每次窗口向右移动 1 位。你的任务是找出每次窗口移动后得到的新窗口中元素的中位数,并输出由它们组成的数组。
示例:
给出 nums = [1,3,-1,-3,5,3,6,7]
,以及 k = 3。
窗口位置 中位数 --------------- ----- [1 3 -1] -3 5 3 6 7 1 1 [3 -1 -3] 5 3 6 7 -1 1 3 [-1 -3 5] 3 6 7 -1 1 3 -1 [-3 5 3] 6 7 3 1 3 -1 -3 [5 3 6] 7 5 1 3 -1 -3 5 [3 6 7] 6
因此,返回该滑动窗口的中位数数组 [1,-1,-1,3,5,6]
。
提示:
k
始终有效,即:k
始终小于等于输入的非空数组的元素个数。10 ^ -5
以内的答案将被视作正确答案。相似题目
原站题解
cpp 解法, 执行用时: 108 ms, 内存消耗: 34.1 MB, 提交时间: 2023-06-13 14:29:20
class Solution {public:priority_queue<int> small;priority_queue<int, vector<int>, greater<int> > big;unordered_map<int, int> mp;double get(int& k){if(k%2) return small.top();else return ((long long)small.top()+big.top())*0.5;}vector<double> medianSlidingWindow(vector<int>& nums, int k) {for(int i = 0; i < k; i++){small.push(nums[i]);};for(int i = 0; i < k / 2; i++){big.push(small.top()); small.pop();}vector<double> ans{get(k)};for(int i = k; i < nums.size(); i++){int balance = 0;int l = nums[i-k];mp[l]++;if(!small.empty() && l<=small.top()){balance--;}else {balance++;}if(!small.empty() && nums[i] <= small.top()){small.push(nums[i]);balance++;}else{big.push(nums[i]);balance--;}if(balance>0){big.push(small.top());small.pop();}if(balance<0){small.push(big.top());big.pop();}while(!small.empty() && mp[small.top()]>0){mp[small.top()]--;small.pop();}while(!big.empty() && mp[big.top()]>0){mp[big.top()]--;big.pop();}ans.push_back(get(k));}return ans;}};
golang 解法, 执行用时: 80 ms, 内存消耗: 11.3 MB, 提交时间: 2023-06-13 14:28:53
type hp struct {sort.IntSlicesize int}func (h *hp) Push(v interface{}) { h.IntSlice = append(h.IntSlice, v.(int)) }func (h *hp) Pop() interface{} { a := h.IntSlice; v := a[len(a)-1]; h.IntSlice = a[:len(a)-1]; return v }func (h *hp) push(v int) { h.size++; heap.Push(h, v) }func (h *hp) pop() int { h.size--; return heap.Pop(h).(int) }func (h *hp) prune() {for h.Len() > 0 {num := h.IntSlice[0]if h == small {num = -num}if d, has := delayed[num]; has {if d > 1 {delayed[num]--} else {delete(delayed, num)}heap.Pop(h)} else {break}}}var delayed map[int]intvar small, large *hpfunc medianSlidingWindow(nums []int, k int) []float64 {delayed = map[int]int{} // 哈希表,记录「延迟删除」的元素,key 为元素,value 为需要删除的次数small = &hp{} // 大根堆,维护较小的一半元素large = &hp{} // 小根堆,维护较大的一半元素makeBalance := func() {// 调整 small 和 large 中的元素个数,使得二者的元素个数满足要求if small.size > large.size+1 { // small 比 large 元素多 2 个large.push(-small.pop())small.prune() // small 堆顶元素被移除,需要进行 prune} else if small.size < large.size { // large 比 small 元素多 1 个small.push(-large.pop())large.prune() // large 堆顶元素被移除,需要进行 prune}}insert := func(num int) {if small.Len() == 0 || num <= -small.IntSlice[0] {small.push(-num)} else {large.push(num)}makeBalance()}erase := func(num int) {delayed[num]++if num <= -small.IntSlice[0] {small.size--if num == -small.IntSlice[0] {small.prune()}} else {large.size--if num == large.IntSlice[0] {large.prune()}}makeBalance()}getMedian := func() float64 {if k&1 > 0 {return float64(-small.IntSlice[0])}return float64(-small.IntSlice[0]+large.IntSlice[0]) / 2}for _, num := range nums[:k] {insert(num)}n := len(nums)ans := make([]float64, 1, n-k+1)ans[0] = getMedian()for i := k; i < n; i++ {insert(nums[i])erase(nums[i-k])ans = append(ans, getMedian())}return ans}
python3 解法, 执行用时: 268 ms, 内存消耗: 30.9 MB, 提交时间: 2023-06-13 14:28:37
class DualHeap:def __init__(self, k: int):# 大根堆,维护较小的一半元素,注意 python 没有大根堆,需要将所有元素取相反数并使用小根堆self.small = list()# 小根堆,维护较大的一半元素self.large = list()# 哈希表,记录「延迟删除」的元素,key 为元素,value 为需要删除的次数self.delayed = collections.Counter()self.k = k# small 和 large 当前包含的元素个数,需要扣除被「延迟删除」的元素self.smallSize = 0self.largeSize = 0# 不断地弹出 heap 的堆顶元素,并且·更新哈希表def prune(self, heap: List[int]):while heap:num = heap[0]if heap is self.small:num = -numif num in self.delayed:self.delayed[num] -= 1if self.delayed[num] == 0:self.delayed.pop(num)heapq.heappop(heap)else:break# 调整 small 和 large 中的元素个数,使得二者的元素个数满足要求def makeBalance(self):if self.smallSize > self.largeSize + 1:# small 比 large 元素多 2 个heapq.heappush(self.large, -self.small[0])heapq.heappop(self.small)self.smallSize -= 1self.largeSize += 1# small 堆顶元素被移除,需要进行 pruneself.prune(self.small)elif self.smallSize < self.largeSize:# large 比 small 元素多 1 个heapq.heappush(self.small, -self.large[0])heapq.heappop(self.large)self.smallSize += 1self.largeSize -= 1# large 堆顶元素被移除,需要进行 pruneself.prune(self.large)def insert(self, num: int):if not self.small or num <= -self.small[0]:heapq.heappush(self.small, -num)self.smallSize += 1else:heapq.heappush(self.large, num)self.largeSize += 1self.makeBalance()def erase(self, num: int):self.delayed[num] += 1if num <= -self.small[0]:self.smallSize -= 1if num == -self.small[0]:self.prune(self.small)else:self.largeSize -= 1if num == self.large[0]:self.prune(self.large)self.makeBalance()def getMedian(self) -> float:return float(-self.small[0]) if self.k % 2 == 1 else (-self.small[0] + self.large[0]) / 2class 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
java 解法, 执行用时: 46 ms, 内存消耗: 54.1 MB, 提交时间: 2023-06-13 14:28:20
class Solution {public double[] medianSlidingWindow(int[] nums, int k) {DualHeap dh = new DualHeap(k);for (int i = 0; i < k; ++i) {dh.insert(nums[i]);}double[] ans = new double[nums.length - k + 1];ans[0] = dh.getMedian();for (int i = k; i < nums.length; ++i) {dh.insert(nums[i]);dh.erase(nums[i - k]);ans[i - k + 1] = dh.getMedian();}return ans;}}class DualHeap {// 大根堆,维护较小的一半元素private PriorityQueue<Integer> small;// 小根堆,维护较大的一半元素private PriorityQueue<Integer> large;// 哈希表,记录「延迟删除」的元素,key 为元素,value 为需要删除的次数private Map<Integer, Integer> delayed;private int k;// small 和 large 当前包含的元素个数,需要扣除被「延迟删除」的元素private int smallSize, largeSize;public DualHeap(int k) {this.small = new PriorityQueue<Integer>(new Comparator<Integer>() {public int compare(Integer num1, Integer num2) {return num2.compareTo(num1);}});this.large = new PriorityQueue<Integer>(new Comparator<Integer>() {public int compare(Integer num1, Integer num2) {return num1.compareTo(num2);}});this.delayed = new HashMap<Integer, Integer>();this.k = k;this.smallSize = 0;this.largeSize = 0;}public double getMedian() {return (k & 1) == 1 ? small.peek() : ((double) small.peek() + large.peek()) / 2;}public void insert(int num) {if (small.isEmpty() || num <= small.peek()) {small.offer(num);++smallSize;} else {large.offer(num);++largeSize;}makeBalance();}public void erase(int num) {delayed.put(num, delayed.getOrDefault(num, 0) + 1);if (num <= small.peek()) {--smallSize;if (num == small.peek()) {prune(small);}} else {--largeSize;if (num == large.peek()) {prune(large);}}makeBalance();}// 不断地弹出 heap 的堆顶元素,并且更新哈希表private void prune(PriorityQueue<Integer> heap) {while (!heap.isEmpty()) {int num = heap.peek();if (delayed.containsKey(num)) {delayed.put(num, delayed.get(num) - 1);if (delayed.get(num) == 0) {delayed.remove(num);}heap.poll();} else {break;}}}// 调整 small 和 large 中的元素个数,使得二者的元素个数满足要求private void makeBalance() {if (smallSize > largeSize + 1) {// small 比 large 元素多 2 个large.offer(small.poll());--smallSize;++largeSize;// small 堆顶元素被移除,需要进行 pruneprune(small);} else if (smallSize < largeSize) {// large 比 small 元素多 1 个small.offer(large.poll());++smallSize;--largeSize;// large 堆顶元素被移除,需要进行 pruneprune(large);}}}
cpp 解法, 执行用时: 108 ms, 内存消耗: 33 MB, 提交时间: 2023-06-13 14:28:05
// 双优先队列 + 延迟删除class DualHeap {private:// 大根堆,维护较小的一半元素priority_queue<int> small;// 小根堆,维护较大的一半元素priority_queue<int, vector<int>, greater<int>> large;// 哈希表,记录「延迟删除」的元素,key 为元素,value 为需要删除的次数unordered_map<int, int> delayed;int k;// small 和 large 当前包含的元素个数,需要扣除被「延迟删除」的元素int smallSize, largeSize;public:DualHeap(int _k): k(_k), smallSize(0), largeSize(0) {}private:// 不断地弹出 heap 的堆顶元素,并且更新哈希表template<typename T>void prune(T& heap) {while (!heap.empty()) {int num = heap.top();if (delayed.count(num)) {--delayed[num];if (!delayed[num]) {delayed.erase(num);}heap.pop();}else {break;}}}// 调整 small 和 large 中的元素个数,使得二者的元素个数满足要求void makeBalance() {if (smallSize > largeSize + 1) {// small 比 large 元素多 2 个large.push(small.top());small.pop();--smallSize;++largeSize;// small 堆顶元素被移除,需要进行 pruneprune(small);}else if (smallSize < largeSize) {// large 比 small 元素多 1 个small.push(large.top());large.pop();++smallSize;--largeSize;// large 堆顶元素被移除,需要进行 pruneprune(large);}}public:void insert(int num) {if (small.empty() || num <= small.top()) {small.push(num);++smallSize;}else {large.push(num);++largeSize;}makeBalance();}void erase(int num) {++delayed[num];if (num <= small.top()) {--smallSize;if (num == small.top()) {prune(small);}}else {--largeSize;if (num == large.top()) {prune(large);}}makeBalance();}double getMedian() {return k & 1 ? small.top() : ((double)small.top() + large.top()) / 2;}};class Solution {public:vector<double> medianSlidingWindow(vector<int>& nums, int k) {DualHeap dh(k);for (int i = 0; i < k; ++i) {dh.insert(nums[i]);}vector<double> ans = {dh.getMedian()};for (int i = k; i < nums.size(); ++i) {dh.insert(nums[i]);dh.erase(nums[i - k]);ans.push_back(dh.getMedian());}return ans;}};