import java.util.*; /** * NC395 滑动窗口中位数 * @author d3y1 */ public class Solution { /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * <p> * 相似 -> NC252 多数组中位数 [nowcoder] * 相似 -> NC131 数据流中的中位数 [nowcoder] * * @param nums int整型ArrayList * @param k int整型 * @return double浮点型ArrayList */ public ArrayList<Double> slidewindow(ArrayList<Integer> nums, int k) { // return solution1(nums, k); // return solution2(nums, k); return solution3(nums, k); } /** * 双堆+双指针: 超时! * * @param nums * @param k * @return */ private ArrayList<Double> solution1(ArrayList<Integer> nums, int k) { int n = nums.size(); if (n < k) { return new ArrayList<>(); } ArrayList<Double> list = new ArrayList<>(); // 双堆 TwoHeap th = new TwoHeap(k); for (int i = 0; i < k; i++) { th.addToHeap(nums.get(i)); } list.add(th.getMedian()); // 双指针 for (int i = 0, j = k; j < n; i++, j++) { th.addToHeap(nums.get(j)); th.removeFromHeap(nums.get(i)); list.add(th.getMedian()); } return list; } /** * 双堆 */ private class TwoHeap { // 大根堆 维护较小的一半元素 private PriorityQueue<Integer> maxHeap; // 小根堆 维护较大的一半元素 private PriorityQueue<Integer> minHeap; // 滑动窗口大小 private int k; public TwoHeap(int k) { this.maxHeap = new PriorityQueue<>(Comparator.reverseOrder()); this.minHeap = new PriorityQueue<>(); this.k = k; } private void addToHeap(int num) { if (minHeap.size() == maxHeap.size()) { minHeap.offer(num); maxHeap.offer(minHeap.poll()); } else { maxHeap.offer(num); minHeap.offer(maxHeap.poll()); } } private void removeFromHeap(int num) { if (num <= maxHeap.peek()) { if (minHeap.size() == maxHeap.size()) { maxHeap.remove(num); maxHeap.offer(minHeap.poll()); } else { maxHeap.remove(num); } } else { if (minHeap.size() == maxHeap.size()) { minHeap.remove(num); } else { minHeap.remove(num); minHeap.offer(maxHeap.poll()); } } } /** * 获取中位数 * * @return */ private double getMedian() { // if(k%2 == 0){ if ((k & 1) == 0) { return (minHeap.peek() + maxHeap.peek()) / 2.0; } else { return maxHeap.peek(); } } } ////////////////////////////////////////////////////////////////////////////////////////////// /** * 双堆+双指针+哈希(延迟删除) * * @param nums * @param k * @return */ private ArrayList<Double> solution2(ArrayList<Integer> nums, int k) { int n = nums.size(); if (n < k) { return new ArrayList<>(); } ArrayList<Double> list = new ArrayList<>(); // 双堆 DualHeap dh = new DualHeap(k); for (int i = 0; i < k; i++) { dh.insert(nums.get(i)); } list.add(dh.getMedian()); // 双指针 for (int i = 0, j = k; j < n; i++, j++) { dh.insert(nums.get(j)); dh.delete(nums.get(i)); list.add(dh.getMedian()); } return list; } /** * 双堆 */ private class DualHeap { // 大根堆 维护较小的一半元素 private PriorityQueue<Integer> maxHeap; // 小根堆 维护较大的一半元素 private PriorityQueue<Integer> minHeap; // 哈希表 记录「延迟删除」的元素, key 为元素, value 为需要删除的次数 private Map<Integer, Integer> delayed; // 滑动窗口大小 private int k; // maxHeap 和 minHeap 当前包含的元素个数, 需要扣除被「延迟删除」的元素 private int maxHeapSize, minHeapSize; public DualHeap(int k) { this.maxHeap = new PriorityQueue<>(Comparator.reverseOrder()); this.minHeap = new PriorityQueue<>(); this.delayed = new HashMap<>(); this.k = k; this.maxHeapSize = 0; this.minHeapSize = 0; } /** * 获取中位数 * * @return */ public double getMedian() { return (k & 1) == 1 ? maxHeap.peek() : (maxHeap.peek() + minHeap.peek()) / 2.0; } /** * 新增元素 * * @param num */ public void insert(int num) { if (minHeapSize == maxHeapSize) { minHeap.offer(num); maxHeap.offer(minHeap.poll()); ++maxHeapSize; prune(minHeap); } else { maxHeap.offer(num); minHeap.offer(maxHeap.poll()); ++minHeapSize; prune(maxHeap); } } /** * 假删元素: 哈希表实现(延迟删除) * * @param num */ public void delete(int num) { delayed.put(num, delayed.getOrDefault(num, 0) + 1); if (num <= maxHeap.peek()) { if (minHeapSize == maxHeapSize) { --maxHeapSize; if (num == maxHeap.peek()) { prune(maxHeap); } maxHeap.offer(minHeap.poll()); --minHeapSize; ++maxHeapSize; prune(minHeap); } else { --maxHeapSize; if (num == maxHeap.peek()) { prune(maxHeap); } } } else { if (minHeapSize == maxHeapSize) { --minHeapSize; if (num == minHeap.peek()) { prune(minHeap); } } else { --minHeapSize; if (num == minHeap.peek()) { prune(minHeap); } minHeap.offer(maxHeap.poll()); --maxHeapSize; ++minHeapSize; prune(maxHeap); } } } /** * 修剪: 真删元素 * 不断地弹出 heap 的堆顶元素, 并且更新哈希表 * * @param 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; } } } } ////////////////////////////////////////////////////////////////////////////////////////////// /** * 双堆+双指针+哈希(延迟删除): 优化 * * @param nums * @param k * @return */ private ArrayList<Double> solution3(ArrayList<Integer> nums, int k) { int n = nums.size(); if (n < k) { return new ArrayList<>(); } ArrayList<Double> list = new ArrayList<>(); // 双堆 DoubleHeap dh = new DoubleHeap(k); for (int i = 0; i < k; i++) { dh.insert(nums.get(i)); } list.add(dh.getMedian()); // 双指针 for (int i = 0, j = k; j < n; i++, j++) { dh.insert(nums.get(j)); dh.delete(nums.get(i)); list.add(dh.getMedian()); } return list; } /** * 双堆 */ private class DoubleHeap { // 大根堆 维护较小的一半元素 private PriorityQueue<Integer> maxHeap; // 小根堆 维护较大的一半元素 private PriorityQueue<Integer> minHeap; // 哈希表, 记录「延迟删除」的元素, key 为元素,value 为需要删除的次数 private Map<Integer, Integer> delayed; // 滑动窗口大小 private int k; // maxHeap 和 minHeap 当前包含的元素个数, 需要扣除被「延迟删除」的元素 private int maxHeapSize, minHeapSize; public DoubleHeap(int k) { this.maxHeap = new PriorityQueue<>(Comparator.reverseOrder()); this.minHeap = new PriorityQueue<>(); this.delayed = new HashMap<>(); this.k = k; this.maxHeapSize = 0; this.minHeapSize = 0; } /** * 获取中位数 * * @return */ public double getMedian() { return (k & 1) == 1 ? maxHeap.peek() : (maxHeap.peek() + minHeap.peek()) / 2.0; } /** * 新增元素 * * @param num */ public void insert(int num) { if (maxHeap.isEmpty() || num <= maxHeap.peek()) { maxHeap.offer(num); ++maxHeapSize; } else { minHeap.offer(num); ++minHeapSize; } balance(); } /** * 假删元素: 哈希表实现(延迟删除) * * @param num */ public void delete(int num) { delayed.put(num, delayed.getOrDefault(num, 0) + 1); if (num <= maxHeap.peek()) { --maxHeapSize; if (num == maxHeap.peek()) { prune(maxHeap); } } else { --minHeapSize; if (num == minHeap.peek()) { prune(minHeap); } } balance(); } /** * 修剪: 真删元素 * 不断地弹出 heap 的堆顶元素, 并且更新哈希表 * * @param 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; } } } /** * 平衡双堆 * 调整 maxHeap 和 minHeap 中的元素个数, 使得二者的元素个数满足要求: * maxHeapSize = minHeapSize + 1 * 或者 * maxHeapSize = minHeapSize */ private void balance() { if (maxHeapSize > minHeapSize + 1) { // maxHeap 比 minHeap 元素多 2 个 minHeap.offer(maxHeap.poll()); --maxHeapSize; ++minHeapSize; // 上面 maxHeap 堆顶元素被移除, 需要进行 prune prune(maxHeap); } else if (maxHeapSize < minHeapSize) { // minHeap 比 maxHeap 元素多 1 个 maxHeap.offer(minHeap.poll()); ++maxHeapSize; --minHeapSize; // 上面 minHeap 堆顶元素被移除,需要进行 prune prune(minHeap); } } } }