[LeetCode] 1825. 求出 MK 平均值(C语言(超时)、C++、Java)***(存着)

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

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

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

请你实现 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);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值