LeetCode:480. 滑动窗口中位数
滑动窗口
难点: 在窗口右移的同时, 还要考虑窗口中的顺序问题, 快速定位到中位数
《风 险 对 冲》:双堆对顶,大堆小堆同时维护
思路:
- 大小顶堆一定思考清楚了。 小顶堆放大数据,大顶堆放小数据,这样才会导致中间的数据在顶堆的最上方,取数据容易
一定维持小顶堆的数据长度跟大顶堆的相同(偶数的时候)或者是比大顶堆大一(基数的时候)
。这样在获取数据的时候才能够非常容易的知道应该取哪个位置的数据。
AC Code
class Solution {
// 从小到大
PriorityQueue<Integer> small = new PriorityQueue<>();
// 从大到小
PriorityQueue<Integer> big = new PriorityQueue<>(Comparator.reverseOrder());
public double[] medianSlidingWindow(int[] nums, int k) {
// 风险对冲
int len = nums.length;
// 初始化
for(int i = 0; i < k; i++) small.add(nums[i]);
for(int i = 0; i < k / 2; i++) big.add(small.poll());
int left = 0, right = k, idx = 0;
double[] ans = new double[len - k + 1];
while(right <= len) {
double mid = getMid(k);
ans[idx++] = mid;
if(right == len) break;
// 更新区间
// 减 left , 增 right
int rm = nums[left++], ad = nums[right++];
if(small.contains(rm)) small.remove(rm);
// 在 big 中
else big.remove(rm);
// 可要可不要
//rb();
// add
small.add(ad);
rb();
}
return ans;
}
public double getMid(int k){
// 偶数
// 这里必须是在里面就强转成 double 了, 不然会超数据范围 [2147483647,2147483647]
if(k % 2 == 0) return ((double)small.peek() + (double)big.peek()) / 2.0;
// 奇数
else return small.peek();
}
public void rb(){
if(small.size() > 0 && big.size() > 0 && big.peek() > small.peek()) {
int tmp = small.peek();
big.add(tmp);
small.remove(tmp);
}
if(small.size() - big.size() > 1) {
int tmp = small.peek();
big.add(tmp);
small.remove(tmp);
}
if(big.size() - small.size() > 0) {
int tmp = big.peek();
small.add(tmp);
big.remove(tmp);
}
}
}
参考: 《风 险 对 冲》:双堆对顶,大堆小堆同时维护,44ms