当前位置: 首页 > article >正文

Java | Leetcode Java题解之第480题滑动窗口中位数

题目:

题解:

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 堆顶元素被移除,需要进行 prune
            prune(small);
        } else if (smallSize < largeSize) {
            // large 比 small 元素多 1 个
            small.offer(large.poll());
            ++smallSize;
            --largeSize;
            // large 堆顶元素被移除,需要进行 prune
            prune(large);
        }
    }
}

http://www.kler.cn/news/355077.html

相关文章:

  • 决策树C4.5算法详解及实现
  • openEuler-22.03-SP4离线编译安装ZLMediaKit
  • A Survey on 3D Gaussian Splatting 整理
  • XML 和 SimpleXML 简介
  • linux环境下的程序设计与git操作
  • 【MySQL】入门篇—基本数据类型:NULL值的概念
  • 利用mydumper从MySQL迁移数据到OceanBase数据库命令记录
  • PHP学习记录-编辑器推荐和本地环境的安装
  • 锁定云轴科技ZStack主题演讲,10月19日中国云计算基础架构开发者大会见
  • WHAT - Antd 定制主题之预设算法
  • 【论文笔记】Fine-tuned CLIP Models are Efficient Video Learners
  • 代替AD作为身份认证组件,深信服零信任aTrust与宁盾身份目录实现互操作
  • leetcode 刷题day43动态规划Part12(115.不同的子序列、583. 两个字符串的删除操作、72. 编辑距离)
  • 一键获取每日股票数据,自动更新,尽在掌握
  • Autosar软件组件概述
  • 数字图像处理:图像复原应用
  • java 调用 k8s 的 apiserver
  • 公开选拔!产业实践教授
  • RHCE--at,crontab例行性工作
  • 滚雪球学Redis[5.3讲]:Redis持久化优化深度解析:RDB与AOF的策略选择与实践