给你两个整数 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 平均数 ,结果需 向下取整到最近的整数 。
示例 1:
输入: ["MKAverage", "addElement", "addElement", "calculateMKAverage", "addElement", "calculateMKAverage", "addElement", "addElement", "addElement", "calculateMKAverage"] [[3, 1], [3], [1], [], [10], [], [5], [5], [5], []] 输出: [null, null, null, -1, null, 3, null, null, null, 5] 解释: MKAverage obj = new MKAverage(3, 1); obj.addElement(3); // 当前元素为 [3] obj.addElement(1); // 当前元素为 [3,1] obj.calculateMKAverage(); // 返回 -1 ,因为 m = 3 ,但数据流中只有 2 个元素 obj.addElement(10); // 当前元素为 [3,1,10] obj.calculateMKAverage(); // 最后 3 个元素为 [3,1,10] // 删除最小以及最大的 1 个元素后,容器为 [3] // [3] 的平均值等于 3/1 = 3 ,故返回 3 obj.addElement(5); // 当前元素为 [3,1,10,5] obj.addElement(5); // 当前元素为 [3,1,10,5,5] obj.addElement(5); // 当前元素为 [3,1,10,5,5,5] obj.calculateMKAverage(); // 最后 3 个元素为 [5,5,5] // 删除最小以及最大的 1 个元素后,容器为 [5] // [5] 的平均值等于 5/1 = 5 ,故返回 5
提示:
3 <= m <= 105
1 <= k*2 < m
1 <= num <= 105
addElement
与calculateMKAverage
总操作次数不超过105
次。
想法:
如果用c硬肝的话,那么你认真读过题之后就知道要选择顺序表解决问题(普通的添加元素,遍历、排序),但是对于最后m个数据的存取,就很有问题了,反复的存计算平均值,数据过大一直反复遍历,就超时了,所以这里还需要用到队列存取(利用特性先进先出),所以这里还要模拟实现队列,C也太难了吧,下面是我的C最初的两种写法,没用队列存储数据是过不去的,当然大家喜欢可以用C语言在实现一下队列进行操作,这一题还是推荐c++/Java
思路(官方题解和我想的差不多):
三个有序集合:
我们使用三个有序集合s1,s2和s3分别保存最小的k个元素、中间m-2k个元素和最大的k个元素;使用sum2保存s2中所有元素之和,最后用队列q保存最后的m个元素。
addElement函数:
- q的元素数目小余等于m:
将新增加的元素num插入有序集合s2中,并且更新sumg2= sum2 + num。如果q的元素数目等于m,那么我们需要分别将s2最小的k个元素和最大的k个元素移动到s1和s3中,同时相应地更新sum2。
- q的元素等于m+1
如果num 小于s1的最大元素,那么我们将它插入s1中,然后将s1的最大元素移动到s2中,同时相应地更新sum2;如果num大于s3的最小元素,那么我们将它插入s3中,然后将s3的最小元素移动到s2中,同时相应地更新sumg;否则我们将num插入 s2中,并且更新 sum2 = sum2 + num。以上操作之后,s2的元素数目额外多出1个,因此删除操作发生在s或s3时,需要从 s2移动元素以保持元素数目的平衡。
我们从队列q的队头取出首元素x。对于待删除的元素x:∶如果x在s2中,我们将它从 s2中删除;如果x在s1中,我们将它从s1中删除,然后将s2中最小的元素移动到s1中;如果x在s3中,我们将它从 s3中删除,然后将s2中最大的元素移动到s3中。
calculateMKAverage函数:
如果队列 qqq 的元素数目小于 mmm,直接返回 −1;否则返回sum/(m-2*k)
C:
版本一:
#define N 100000
typedef struct {
int size,k,m,capacity;
int* data;
} MKAverage;
//这里重写cmp方法 比较m大小
int cmp(const void*e1,const void*e2){
return (*(int*)e1)-(*(int*)e2);
}
//初始化 创建对象
MKAverage* mKAverageCreate(int m, int k) {
MKAverage* obj = (MKAverage*)malloc(sizeof(MKAverage));
assert(obj!=NULL);
obj->size = 0;
obj->k = k;
obj->m = m;
obj->capacity = N;
obj->data = (int*)malloc(sizeof(int)*N);
return obj;
}
//添加数据
void mKAverageAddElement(MKAverage* obj, int num) {
if(obj->size == obj->capacity){//扩容
obj->capacity+=10000;
int* tmp = (int*)realloc(obj->data, obj->capacity * sizeof(int));
assert(tmp!=NULL);
obj->data = tmp;
}
obj->data[obj->size++] = num;
}
//求MK平均值
int mKAverageCalculateMKAverage(MKAverage* obj) {
int m = obj->m;
int k = obj->k;
int size = obj->size;
if(size<m) return -1;//这就是整数小余m个的条件
int* mk = (int*)malloc(sizeof(int)*m);//容器存储最后m个元素
for(int i = size-m,j=0;i<size;i++,j++){
mk[j] = obj->data[i];
}
qsort(mk,m,sizeof(int),cmp);//排序
int res = 0;
for(int i = k;i<m-k;i++) res+=mk[i];
return res/(m-2*k);
}
//释放内存
void mKAverageFree(MKAverage* obj) {
free(obj->data);
obj->size = obj->k = obj->m = obj->capacity = 0;
obj->data = NULL;
}
版本二(画蛇添足依旧超时):
#define N 100000
//全局变量 对于存贮最后m个元素 是有规律性的
int* mk;//容器存储最后m个元素
int first;//第size-m-1个元素
bool flag = false;//这个记录是否已经可以满足求平均值条件
typedef struct {
int size,k,m,capacity;
int* data;
} MKAverage;
//这里重写cmp方法 比较m大小
int cmp(const void*e1,const void*e2){
return (*(int*)e1)-(*(int*)e2);
}
//初始化 创建对象
MKAverage* mKAverageCreate(int m, int k) {
MKAverage* obj = (MKAverage*)malloc(sizeof(MKAverage));
assert(obj!=NULL);
obj->size = 0;
obj->k = k;
obj->m = m;
obj->capacity = N;
obj->data = (int*)malloc(sizeof(int)*N);
mk = (int*)malloc(sizeof(int)*m);//容器存储最后m个元素
return obj;
}
void replace(MKAverage* obj,int num){
for(int i = 0;i<obj->m;i++){
if(mk[i]==first) mk[i] = num;
}
}
//添加数据
void mKAverageAddElement(MKAverage* obj, int num) {
if(obj->size == obj->capacity){//扩容
obj->capacity+=N;
int* tmp = (int*)realloc(obj->data, obj->capacity * sizeof(int));
assert(tmp!=NULL);
obj->data = tmp;
}
obj->data[obj->size++] = num;
if(flag) replace(obj,num);
}
//求MK平均值
int mKAverageCalculateMKAverage(MKAverage* obj) {
int m = obj->m;
int k = obj->k;
int size = obj->size;
if(size<m) return -1;//这就是整数小余m个的条件
if(!flag){
for(int i = size-m,j=0;i<size;i++,j++){
mk[j] = obj->data[i];
}
}
qsort(mk,m,sizeof(int),cmp);//排序
int res = 0;
for(int i = k;i<m-k;i++) res+=mk[i];
return res/(m-2*k);
}
//释放内存
void mKAverageFree(MKAverage* obj) {
free(obj->data);
obj->size = obj->k = obj->m = obj->capacity = 0;
obj->data = NULL;
}
//本来想用c语言就写出来装个杯 把自己写恶心了
//不想写 摆烂了 直接cv
C++:
class MKAverage {
private:
// 长度为m
int m;
// 删除首尾的k个
int k;
private:
long long sum = 0;
queue<int> q;
multiset<int> minn, mid, maxx;
public:
MKAverage(int m, int k) :m(m), k(k) {
}
void addElement(int num) {
q.push(num);
mid.insert(num);
sum += num;
// 还未到达目标
if (q.size() < m) {
}
// 可以算出avg
else if (q.size() == m) {
// 取出最小的k个
for (int i = 0; i < k; i += 1) {
int x = *mid.begin();
sum -= x;
minn.insert(x);
mid.erase(mid.begin());
}
// 取出最大的k个
for (int i = 0; i < k; i += 1) {
int x = *mid.rbegin();
sum -= x;
maxx.insert(x);
// erase 不能用逆向迭代器
mid.erase(--mid.end());
}
}
// 进行维护
else {
int pop = q.front();
q.pop();
// 下面均要使用find找出具体位置
// 在小集合内
if (pop <= *minn.rbegin()) {
minn.erase(minn.find(pop));
int x = *mid.begin();
sum -= x;
minn.insert(x);
mid.erase(mid.begin());
}
// 在大集合内
else if (pop >= *maxx.begin()) {
maxx.erase(maxx.find(pop));
int x = *mid.rbegin();
sum -= x;
maxx.insert(x);
mid.erase(--mid.end());
}
// 在中间,需要维护sum
else {
mid.erase(mid.find(pop));
sum -= pop;
}
// 另外要检查新加入的元素是否是minn,maxx
if (*mid.begin() < *minn.rbegin()) {
int midNum = *mid.begin();
mid.erase(mid.begin());
int minnNum = *minn.rbegin();
minn.erase(--minn.end());
sum -= midNum;
sum += minnNum;
mid.insert(minnNum);
minn.insert(midNum);
}
else if (*mid.rbegin() > *maxx.begin()) {
int midNum = *mid.rbegin();
mid.erase(--mid.end());
int maxxNum = *maxx.begin();
maxx.erase(maxx.begin());
sum -= midNum;
sum += maxxNum;
mid.insert(maxxNum);
maxx.insert(midNum);
}
}
}
int calculateMKAverage() {
if (q.size() < m) {
return -1;
}
return (sum) / (m - 2 * k);
}
};
Java:
int m, k, div;
long sum;
Queue<Integer> q;
TreeMap<Integer, Integer> s1, s2, s3;
int size1, size2, size3;
public MKAverage(int m, int k) {
this.m = m;
this.k = k;
div = m - 2 * k;
q = new ArrayDeque<>(); //存储数据流
s1 = new TreeMap<>(); //存储最小的 k 个数
s2 = new TreeMap<>(); //存储m - 2k 个数
s3 = new TreeMap<>(); //存储最大的 k 个数
sum = 0;
size1 = 0;
size2 = 0;
size3 = 0;
}
public void addElement(int num) {
q.offer(num);
if(q.size() <= m){
s2.put(num, s2.getOrDefault(num, 0) + 1);
sum += num;
++size2;
if(q.size() == m){
//移出 s2 中最小的 k 个数 到 s1
int count = 0;
while(size1 < k){
int first = s2.firstKey();
s1.put(first, s1.getOrDefault(first, 0) + 1);
sum -= first;
--size2;
++size1;
count = s2.get(first);
s2.put(first, count - 1);
if(count == 1) s2.remove(first);
}
//移出 s2 中最大的 k 个数 到 s3
while(size3 < k){
int last = s2.lastKey();
s3.put(last, s3.getOrDefault(last, 0) + 1);
sum -= last;
--size2;
++size3;
count = s2.get(last);
s2.put(last, count - 1);
if(count == 1) s2.remove(last);
}
}
return;
}
int count = 0;
if(num < s1.lastKey()){ //当前数字小于最小的k个数中的最大数 当前数字属于最小的 k 个数范围
s1.put(num, s1.getOrDefault(num, 0) + 1);
int last = s1.lastKey(); //将 num移动到 s1, s1 中的最大数 移出
count = s1.get(last);
s1.put(last, count - 1);
if(count == 1) s1.remove(last);
s2.put(last, s2.getOrDefault(last, 0) + 1); //将 s1 中的最大数 移到 s2
++size2;
sum += last;
}else if(num > s3.firstKey()){ //当前数字大于最大的k个数中的最小数 当前数字属于最大的 k 个数范围
s3.put(num, s3.getOrDefault(num, 0) + 1);
int first = s3.firstKey(); //将 num 移动到 s3, s3中最小数移出
count = s3.get(first);
s3.put(first, count - 1);
if(count == 1) s3.remove(first);
s2.put(first, s2.getOrDefault(first, 0) + 1);//将 s3 中的最小数移动到 s2
++size2;
sum += first;
}else{ //当前数字 不属于其他两个范围内 直接放入 s2
s2.put(num, s2.getOrDefault(num, 0) + 1);
++size2;
sum += num;
}
//处理流中多出的数字 x
int x = q.poll();
if(s1.containsKey(x)){ //多出来的 数 在 最小的 k 个数范围
count = s1.get(x); //s1 中 移除 x
s1.put(x, count - 1);
if(count == 1) s1.remove(x);
int first = s2.firstKey(); //将 s2 中 最小数 移到 s1 中
s1.put(first, s1.getOrDefault(first, 0) + 1);
count = s2.get(first);
s2.put(first, count - 1);
if(count == 1) s2.remove(first);
--size2;
sum -= first;
}else if(s3.containsKey(x)){ //多出来的数 在 最大的 k 个数范围内
count = s3.get(x); //s3 中 移除 x
s3.put(x, count - 1);
if(count == 1) s3.remove(x);
int last = s2.lastKey(); //将 s2 中 最大数 移到 s3 中
s3.put(last, s3.getOrDefault(last, 0) + 1);
count = s2.get(last);
s2.put(last, count - 1);
if(count == 1) s2.remove(last);
--size2;
sum -= last;
}else{
count = s2.get(x);
s2.put(x, count - 1);
if(count == 1) s2.remove(x);
--size2;
sum -= x;
}
}
public int calculateMKAverage() {
if(q.size() < m) return -1;
return (int)(sum / div);
}