求出 MK 平均值【LC1825】
给你两个整数
m
和k
,以及数据流形式的若干整数。你需要实现一个数据结构,计算这个数据流的 MK 平均值 。MK 平均值 按照如下步骤计算:
- 如果数据流中的整数少于
m
个,MK 平均值 为-1
,否则将数据流中最后m
个元素拷贝到一个独立的容器中。- 从这个容器中删除最小的
k
个数和最大的k
个数。- 计算剩余元素的平均值,并 向下取整到最近的整数 。
请你实现
MKAverage
类:
MKAverage(int m, int k)
用一个空的数据流和两个整数m
和k
初始化 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,m−k−1]个数据之和,然后计算平均值
-
实现【超时】
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)。
-
实现
维护一下数据结构和变量
- 使用三个有序集合TreeMap
min,mid,max
分别存储最小的k个元素、中间的元素、最大的k个元素,key值为元素大小,value值为元素个数,因此还需要使用额外变量记录集合中元素的个数 - 使用一个队列记录当前的m个元素,队列头部为最早加入的元素,队尾元素为最近加入的元素
- 使用一个int类型变量记录中间元素之和
函数实现
-
addElement
- 加入元素
num
值对应位置,并放入队列中:- 如果
min
为空,或者 n u m ≤ m a x ( m i n ) num \le max(min) num≤max(min),则将num
加入min
中 - 否则,如果
max
为空,或者 n u m ≥ m i n ( m a x ) num \ge min(max) num≥min(max),则将num
加入max
中 - 否则,则将
num
加入mid
中,sum
增加num
- 如果
- 移除元素:
- 如果队列的长度大于
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/(m−2∗k)
- 使用三个有序集合TreeMap
-
实现
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(); */