【每日一题Day91】LC1825求出 MK 平均值 | 有序集合+模拟

求出 MK 平均值【LC1825】

给你两个整数 mk ,以及数据流形式的若干整数。你需要实现一个数据结构,计算这个数据流的 MK 平均值

MK 平均值 按照如下步骤计算:

  1. 如果数据流中的整数少于 m 个,MK 平均值-1 ,否则将数据流中最后 m 个元素拷贝到一个独立的容器中。
  2. 从这个容器中删除最小的 k 个数和最大的 k 个数。
  3. 计算剩余元素的平均值,并 向下取整到最近的整数

请你实现 MKAverage 类:

  • MKAverage(int m, int k) 用一个空的数据流和两个整数 mk 初始化 MKAverage 对象。
  • void addElement(int num) 往数据流中插入一个新的元素 num
  • int calculateMKAverage() 对当前的数据流计算并返回 MK 平均数 ,结果需 向下取整到最近的整数

重新投入学习

数组+排序

  • 思路:使用一个长度为 m m m的数组记录数据流的最近 m m m位数据,然后每次计算平均值时,将数据排序,然后计算中间 [ k + 1 , m − k − 1 ] [k+1,m-k-1] [k+1,mk1]个数据之和,然后计算平均值

  • 实现【超时】

    i i i为总共插入的元素下标,通过取余的方式覆盖之前的元素

    class MKAverage {
        int[] nums;
        int[] sort;
        int i, m, k;
        public MKAverage(int m, int k) {
            nums = new int[m];
            sort = new int[m];
            i = 0;
            this.m = m;
            this.k = k;
        }
        
        public void addElement(int num) {
            nums[i % m] = num;
            i++;
        }
        
        public int calculateMKAverage() {
            if (i < m) return -1;
            for (int i = 0; i < m; i++){
                sort[i] = nums[i];
            }
            Arrays.sort(sort);       
            int sum = 0;
            for (int i = k; i < m - k; i++){
                sum += sort[k];
            }
            
            return sum / (m - 2 * k);
        }
    }
    
    /**
     * Your MKAverage object will be instantiated and called as such:
     * MKAverage obj = new MKAverage(m, k);
     * obj.addElement(num);
     * int param_2 = obj.calculateMKAverage();
     */
    
    • 复杂度

      • 时间复杂度:addElement函数的时间复杂度为 O ( 1 ) O(1) O(1)。calculateMKAverage函数的时间复杂度为 O ( m l o g m ) O(mlogm) O(mlogm)
      • 空间复杂度: O ( m ) O(m) O(m)

TreeMap

  • 思路:排序后的数组可以分为三个部分:较小的前k个元素、中间元素、前大的后k个元素,因此使用3个有序集合存储这三个部分。在加入元素时,判断元素的个数以及元素的大小移动元素,时间复杂度为 O ( l o g m ) O(logm) O(logm);在求平均值时,直接求取中间元素的平均值,时间复杂度为 O ( 1 ) O(1) O(1)

  • 实现

    维护一下数据结构和变量

    • 使用三个有序集合TreeMapmin,mid,max分别存储最小的k个元素、中间的元素、最大的k个元素,key值为元素大小,value值为元素个数,因此还需要使用额外变量记录集合中元素的个数
    • 使用一个队列记录当前的m个元素,队列头部为最早加入的元素,队尾元素为最近加入的元素
    • 使用一个int类型变量记录中间元素之和

    函数实现

    • addElement

      1. 加入元素num值对应位置,并放入队列中:
        • 如果min为空,或者 n u m ≤ m a x ( m i n ) num \le max(min) nummax(min),则将num加入min
        • 否则,如果max为空,或者 n u m ≥ m i n ( m a x ) num \ge min(max) nummin(max),则将num加入max
        • 否则,则将num加入mid中,sum增加num
      2. 移除元素:
        • 如果队列的长度大于m,那么移除队首元素,查找队首元素位于哪个集合中,并从该集合中删除该元素,如果该集合为mid,则sum减去队首元素
        • 并判断三个集合中的元素个数是否大于或者小于k,若大于k,那么先移动元素至mid,再将元素移动至min或者max
          • 如果min的长度大于k,则将最大值放入mid
          • 如果max的长度大于k,则将最小值放入mid
          • 如果min的长度小于k,则将mid的最小值放入min
          • 如果max的长度小于k,则将mid的最大值放入max
    • calculateMKAverage

      如果队列长度小于m,那么返回-1,否则返回 s u m / ( m − 2 ∗ k ) sum/(m-2*k) sum/(m2k)

  • 实现

    class MKAverage {
        TreeMap<Integer,Integer> min = new TreeMap<>();
        TreeMap<Integer,Integer> mid = new TreeMap<>();
        TreeMap<Integer,Integer> max = new TreeMap<>();
        Deque<Integer> dq = new LinkedList<>();
        int sum = 0, s1 = 0, s2 = 0, s3 = 0;
        int m, k;
        public MKAverage(int m, int k) {
            this.m = m;
            this.k = k;
        }
        
        public void addElement(int num) {
            // 放入有序集合中
            if (s1 == 0 || num <= min.lastKey()){
                min.merge(num, 1, Integer::sum);
                s1++;
            }else if (s3 == 0 || num >= max.firstKey()){
                max.merge(num, 1, Integer::sum);
                s3++;
            }else{
                mid.merge(num, 1, Integer::sum);
                sum += num;
                s2++;
            }
            // 放入队列中
            dq.addLast(num);
            // 删除队首元素
            if (dq.size() > m){
                int poll = dq.pollFirst();
                if (min.containsKey(poll)){
                    if (min.merge(poll, -1, Integer::sum) == 0){
                        min.remove(poll);
                    }
                    s1--;
                }else if (max.containsKey(poll)){
                    if (max.merge(poll, -1, Integer::sum) == 0){
                        max.remove(poll);
                    }
                    s3--;
                }else{
                    if (mid.merge(poll, -1, Integer::sum) == 0){
                        mid.remove(poll);
                    }
                    s2--;
                    sum -= poll;
                }
            }
            // 调整有序集合中的元素
            while (s1 > k){
                int x = min.lastKey();
                if (min.merge(x, -1 ,Integer::sum) == 0){
                    min.remove(x);
                }
                mid.merge(x, 1, Integer::sum);
                sum += x;
                s2++;
                s1--;
            }
            while (s3 > k){
                int x = max.firstKey();
                if (max.merge(x, -1 ,Integer::sum) == 0){
                    max.remove(x);
                }
                mid.merge(x, 1, Integer::sum);
                sum += x;
                s2++;
                s3--;
            }
            while (s1 < k && !mid.isEmpty()){
                int x = mid.firstKey();
                if (mid.merge(x, -1 ,Integer::sum) == 0){
                    mid.remove(x);
                }
                sum -= x;
                min.merge(x, 1, Integer::sum);
                s2--;           
                s1++;
            }
            while (s3 < k && !mid.isEmpty()){
                int x = mid.lastKey();
                if (mid.merge(x, -1 ,Integer::sum) == 0){
                    mid.remove(x);
                }
                sum -= x;
                max.merge(x, 1, Integer::sum);
                s2--;           
                s3++;
            }
        }
        
        public int calculateMKAverage() {
            if (dq.size() < m){
                return -1;
            }
            return sum / (m - 2 * k);
            
        }
    }
    
    /**
     * Your MKAverage object will be instantiated and called as such:
     * MKAverage obj = new MKAverage(m, k);
     * obj.addElement(num);
     * int param_2 = obj.calculateMKAverage();
     */
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值