Leetcode 1825 对数据流求解MK值
来源:1-5 大厂面试为什么总考算法?
甲骨文_0001
2023-01-18
/**
* @param {number} m
* @param {number} k
*/
var MKAverage = function(m, k) {
this.data = [];
this.m = m;
this.k = k;
};
/**
* @param {number} num
* @return {void}
*/
MKAverage.prototype.addElement = function(num) {
// 先添加
this.data.push(num);
};
/**
* @return {number}
*/
MKAverage.prototype.calculateMKAverage = function() {
// < m, =>-1
if( this.data.length < this.m ){
return -1;
}
// 数据流this.data元素大于m个
let mArr = [];
for( let i = this.data.length - this.m; i < this.data.length; i ++ ){
mArr.push(this.data[i]);
} // for i
// 对mArr进行从小到大排序
mArr.sort((a, b)=>a - b);
// mArr是有序数组, 从小到大
// 先计算总和
let totalSum = mArr.reduce((tmp, cur)=>tmp + cur);
// 最小的k个数
let minKArr = [];
for( let i = 0; i < this.k; i ++ ){
minKArr.push(mArr[i]);
} // for i
let minSum = minKArr.reduce((tmp, cur)=>tmp + cur); // 最小k个数之和
// 最大的k个数
let maxKArr = [];
for( let i = mArr.length - this.k; i < mArr.length; i ++ ){
maxKArr.push( mArr[i] );
} // for i
let maxSum = maxKArr.reduce((tmp, cur)=>tmp+cur); // 最大k个数之和
let leftSum = totalSum - minSum - maxSum;
let leftNum = mArr.length - this.k - this.k;
return Math.floor( leftSum / leftNum );
};
/**
* Your MKAverage object will be instantiated and called as such:
* var obj = new MKAverage(m, k)
* obj.addElement(num)
* var param_2 = obj.calculateMKAverage()
*/

老师,我想我的方法应该是正确的,只是超时了,我采取的比较直接,对数据流 this.data,每进来一个数据, this.data.push(element), 之后,如果要求解calculateMKAverage, 那么我就取出this.data的最后 m 个元素拷贝到一个独立的容器(mArr)中,然后对mArr进行从小到大排序,然后减掉最大与最小的k个元素,再取平均值。因为数据规模是 10^5,级别,脑海中也想到了超时,但不知道更好的方案,哈哈哈。老师,指导下哈。另外祝老师还有您的家人新年快乐: )
1回答
-
liuyubobobo
2023-01-19
其实你问的很多问题都有类似之处,简而言之就是,对于每一个 query,去暴力做肯定不行(要是这就可以的话,这个问题的意义就不大了。)
优化的思路基本上都是:解决当前的 query,能否利用之前 query 的信息,而不是重新完整计算一次。
==========
根据题意:
1)你需要能够快速获得每个元素进来的时间,以快速的在有 m + 1 个元素的时候,删除掉最早进来的一个元素,保证当前的集合里只有 m 个元素;
双端队列可以满足这个需求。
2)你需要能够快速获得当前的 m 个元素的最小的 k 个值,最大的 k 个值,和中间的 m - 2 * k 个值;并且根据 1),你需要在新来一个元素,或者扔掉一个元素的时候,能够维护这三个“集合”。
叙述方便,我叫最小 k 个值的集合是 A;中间 m - 2 * k 个值的集合是 B;最大 k 个值的集合是 C。
这里的重点是要“维护”,你可以想象,为了维护这三个集合,这三个集合中的元素需要互相“腾挪”。比如 A 集合里多一个元素的话(有 k + 1 个元素),就需要把 A 集合的最大值扔到 B 集合,然后再维护 B 集合,以此类推。
所以,存储这些集合的数据结构,需要能快速获得当前集合的最大值或者最小值,包括能快速做增删查。
使用红黑树最合适。
3)你需要能快速计算集合 B 中的元素和。这很简单,在维护集合 B 的时候,同时维护集合 B 的和就好了。
==========
数据结构定下来了,下面的实现在我看来其实挺机械的。我不排除我的代码写复杂了,但整体思路应该很清晰,可以参考:https://github.com/liuyubobobo/Play-Leetcode/blob/master/1501-2000/1825-Finding-MK-Average/cpp-1825/main.cpp
其中“看似”最复杂的两个函数 remove 和 add,就是在新来一个元素的时候,维护 A, B, C(在我的代码中叫 minTree, middleTree 和 maxTree)。
在删除的时候,如果是从 C 中删除,直接删;(42-46)
如果从 B 中删除,把 C 中的最小元素扔到 B;(48-62)
如果从 A 中删除,把 B 中的最小元素扔到 A,再把 C 中的最小元素扔到 B (64-81)
在添加的时候,直接添加到 A (86)
如果 A 的元素多了,把 A 中最大的元素扔到 B;(88-94)
如果 B 中的元素多了,把 B 中的元素扔到 C (96-102)
在这个基础上,题目要求的那两个函数应该是非常简单的:
addElement (27-38)
calculateMK(105-108)
==========
继续加油!:)
122023-01-19
相似问题
回答 3
回答 1