480. Sliding Window Median
今天这道题是一道Hard的题目,给定一个特定大小的滑动窗口以及一个数组,每一次向后滑动一个位置,要求返回一个有着所有的中位数的数组。
我们维护两个堆来解决这个问题,一个大堆一个小堆,我们保证当K为奇数的时候中位数是大堆顶端的数字,当K为偶数的时候,中位数则是大堆顶端和小堆顶端的数字和除以2.
我们在向堆内添加和删除数字的时候都要尽量保持两个堆的大小相等或者相差不大于1。
在向堆内添加数字的时候,如果当前的数字比大堆顶部的数字要小则将其加入大堆中,如果比大堆顶部的数字要大则加入小堆中。
那我们应该如何保证,两个堆的大小相等呢或相差不大于一呢?很简单,当我们每一次添加或者是删除的时候,检测一下两个堆的大小,讲比较大的那个pop出来,Push进小的里面即可。
让我们看一下代码
class Solution {
//创建大小堆
private PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Collections.reverseOrder());
private PriorityQueue<Integer> minHeap = new PriorityQueue<>();
public double[] medianSlidingWindow(int[] nums, int k) {
double[] res =new double[nums.length-k+1];
for(int i = 0;i < nums.length;i++){
add(nums[i]);
if(i+1 >= k){
res[i-k+1] = getMedian();
remove(nums[i-k+1]);
}
}
return res;
}
private double getMedian(){
//根据大小来计算当前中位数
return maxHeap.size() > minHeap.size()?maxHeap.peek():(maxHeap.peek()/2.0+ minHeap.peek()/2.0);
}
private void add(int num){
//向大小堆加入
if(maxHeap.size() == 0 || maxHeap.peek() >= num){
maxHeap.add(num);
}else{
minHeap.add(num);
}
rebalanceHeaps();
}
private void remove(int num){
//移除
if(num <= maxHeap.peek()){
maxHeap.remove(num);
}else{
minHeap.remove(num);
}
rebalanceHeaps();
}
private void rebalanceHeaps(){
//维护大小堆的长度
if(maxHeap.size() == minHeap.size()){
return;
}
if(maxHeap.size() > minHeap.size() + 1){
minHeap.add(maxHeap.poll());
}else if(maxHeap.size() < minHeap.size()){
maxHeap.add(minHeap.poll());
}
}
}