在金融市场的实时交易中,分析师需要剔除极端报价后计算公平价格;在社交媒体的热门内容筛选中,平台需要过滤掉最高和最低的极端评分。这些场景都在重复一个数学问题:如何从流动的数据中,动态地排除极端值后计算有意义的平均值?

今天,让我们一同探索这个看似简单却蕴含深意的算法挑战——MK平均值问题。这不仅是技术面试中的经典考题,更是现代数据处理系统中不可或缺的核心模式。、

题目描述:
给你两个整数 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 平均数 ,结果需 向下取整到最近的整数 。

生活场景引导:从股票交易到内容推荐

想象你是一名股票分析师,需要计算某支股票最近100个交易日的"公平价格"。直接取平均值会受到极端暴涨暴跌的影响,于是你决定剔除最高和最低的10个交易日后计算均值。这就是MK平均值在现实世界中的典型应用。

然而,当数据以流式方式不断涌入时,问题变得复杂:每来一个新的价格,就需要重新计算最近100个交易日的调整后平均值,且要高效处理,不能每次都从头排序。

问题本质与算法分析

问题重述与核心挑战

MK平均值问题的本质是:维护一个大小为m的滑动窗口,对于窗口内的元素,在排除k个最小值和k个最大值后,计算剩余元素的平均值。

输入约束分析

  • 窗口大小m:3 ≤ m ≤ 10^5

  • 排除数量k:满足 2k < m,确保排除后仍有剩余元素

  • 操作次数:最多10^5次

这意味着我们需要在O(1)或O(log n)级别完成每次操作,暴力解法显然不可行。

算法设计思路

暴力解法的局限性
最简单的想法是维护一个数组存储最近m个元素,每次计算时排序并排除极端值。这种方法的复杂度为O(m log m),在m达到10^5时完全无法接受。

多数据结构协同策略
最优解法需要多种数据结构的精妙配合:

  1. 滑动窗口维护:使用队列(deque)维护最近m个元素,保证FIFO特性

  2. 元素分类管理:将窗口内元素分为三个有序集合:

    • 最小k个元素集合

    • 中间m-2k个元素集合

    • 最大k个元素集合

  3. 平衡数据结构选择:使用平衡BST或堆来维护这些集合,保证高效的插入、删除和查询操作

具体实现框架

  • 使用三个多重集合(small, mid, large)分别存储最小k个、中间m-2k个、最大k个元素

  • 维护各集合的元素和,便于快速计算平均值

  • 添加新元素时,确定其应属的集合,并调整各集合以保持大小约束

  • 移除旧元素时,从对应集合中删除,并重新平衡

时间复杂度分析

添加元素(addElement)

  • 确定插入位置:O(log k) 或 O(log m)

  • 可能的集合间元素调整:O(log m)

  • 总体复杂度:O(log m)

计算平均值(calculateMKAverage)

  • 直接返回中间集合的和除以数量:O(1)

空间复杂度分析
  • 存储最近m个元素:O(m)

  • 三个平衡数据结构:O(m)

  • 总体空间复杂度:O(m)

进阶难点解析

难点一:动态集合维护的精确性

核心挑战在于添加新元素和移除旧元素时,如何保持三个集合的大小约束:
- small.size() == k
- large.size() == k  
- mid.size() == m - 2k

每次操作后都需要检查并重新平衡,这需要精妙的边界条件处理。

难点二:元素归属判断的复杂性

新元素到来时,需要快速判断它应该进入哪个集合:
1. 如果num < small的最大值,应进入small
2. 如果num > large的最小值,应进入large  
3. 否则进入mid

同时要考虑各集合当前的大小限制。

难点三:删除操作的同步性

当窗口滑动需要删除旧元素时,必须:
1. 确定该元素在哪个集合中
2. 从对应集合中删除
3. 重新平衡各集合大小

这需要在添加时就记录元素的归属信息。

难点四:数值溢出的预防

元素和可能达到10^5 × 10^5 = 10^10,在32位整数范围内可能溢出。
需要使用64位整数存储求和结果。

算法优化深度探讨

数据结构的选择权衡

方案一:平衡二叉搜索树

  • 优点:支持排名查询、范围查询,便于确定元素位置

  • 缺点:实现复杂,常数因子较大

方案二:堆与延迟删除

  • 使用最大堆维护small,最小堆维护large

  • 中间部分用平衡树或双堆配合延迟删除

  • 优点:实现相对简单

  • 缺点:延迟删除增加逻辑复杂度

方案三:Fenwick树或线段树

  • 基于值域的数据结构,适合元素范围已知的情况

  • 优点:查询效率高

  • 缺点:空间消耗与值域相关

边界情况的精妙处理

初始化阶段

前m-1次addElement调用时,calculateMKAverage返回-1
需要精确计数当前元素数量,避免过早计算

相等元素的处理

当存在多个相同值的元素时,删除哪个?
必须保证删除的是最早加入的相同值元素,这需要额外的标识机制。
性能极限制约分析

给定约束下最坏情况:

  • m = 10^5, k = 49999

  • 操作次数10^5次

每次操作必须在微秒级别完成,这排除了所有O(m)复杂度的算法。

现实世界的应用延伸

金融技术

  • 股票价格过滤异常波动

  • 外汇交易中的报价清洗

物联网数据处理

  • 传感器数据异常值剔除

  • 设备监控中的噪声过滤

社交网络分析

  • 用户评分系统的公平计算

  • 内容热度的合理评估

算法思维的价值启示

MK平均值问题教会我们的不仅是技术实现,更重要的是一种系统思维:

  1. 分解复杂问题:将大问题拆解为数据维护、极端值排除、平均值计算等子问题

  2. 权衡时空效率:在时间复杂度和空间复杂度间寻找最佳平衡点

  3. 预见边界条件:提前识别并处理各种极端情况和边界条件

  4. 选择合适工具:根据问题特性选择最匹配的数据结构和算法

题目验证案例
示例 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

MK平均值问题就像数据世界中的一位智慧裁判,它教会我们在信息的洪流中保持理性,既倾听多数的声音,又警惕极端的干扰。这种平衡的智慧,不仅在算法设计中珍贵,在人生的决策中同样重要。

下一次当你面对复杂的数据或困难的选择时,不妨想想MK平均值的哲学:关注主体,排除极端,在动态平衡中寻找真理。

"数据不会说谎,但需要我们智慧地解读。" —— 在算法的世界里,每一个精妙的设计都是对人类思维的致敬。

附带题目代码:

#include 
#include 
#include 
#include 
// 定义多重集合节点结构
typedef struct MultiSetNode {
    int value;                    // 节点存储的整数值
    int count;                    // 该值出现的次数
    struct MultiSetNode *left;    // 左子树指针
    struct MultiSetNode *right;   // 右子树指针
    int height;                   // 节点高度,用于平衡二叉树
} MultiSetNode;
// 定义多重集合结构
typedef struct {
    MultiSetNode *root;           // 多重集合的根节点
    int total_count;              // 集合中元素的总个数
    long long total_sum;          // 集合中所有元素的和
} MultiSet;
// 定义MKAverage数据结构
typedef struct {
    int m;                        // 滑动窗口大小
    int k;                        // 要排除的最小和最大元素个数
    int *window;                  // 存储滑动窗口元素的数组
    int window_size;              // 当前窗口中元素个数
    int window_index;             // 下一个要插入的位置索引
    MultiSet small_set;           // 存储最小k个元素的多重集合
    MultiSet mid_set;             // 存储中间m-2k个元素的多重集合
    MultiSet large_set;           // 存储最大k个元素的多重集合
} MKAverage;
// 工具函数:获取节点高度
int getHeight(MultiSetNode *node) {
    if (node == NULL) return 0;   // 空节点高度为0
    return node->height;          // 返回节点高度
}
// 工具函数:获取两个整数中的较大值
int max(int a, int b) {
    return (a > b) ? a : b;      // 返回较大的值
}
// 工具函数:创建新节点
MultiSetNode* createNode(int value) {
    MultiSetNode *newNode = (MultiSetNode*)malloc(sizeof(MultiSetNode));  // 分配内存
    newNode->value = value;       // 设置节点值
    newNode->count = 1;           // 初始计数为1
    newNode->left = NULL;         // 左子树初始为空
    newNode->right = NULL;        // 右子树初始为空
    newNode->height = 1;          // 新节点高度为1
    return newNode;               // 返回新节点
}
// 工具函数:右旋操作,用于平衡二叉树
MultiSetNode* rightRotate(MultiSetNode *y) {
    MultiSetNode *x = y->left;    // x是y的左子节点
    MultiSetNode *T2 = x->right;  // T2是x的右子节点
    x->right = y;                 // 将y作为x的右子节点
    y->left = T2;                 // 将T2作为y的左子节点
    y->height = max(getHeight(y->left), getHeight(y->right)) + 1;  // 更新y的高度
    x->height = max(getHeight(x->left), getHeight(x->right)) + 1;  // 更新x的高度
    return x;                     // 返回新的根节点
}
// 工具函数:左旋操作,用于平衡二叉树
MultiSetNode* leftRotate(MultiSetNode *x) {
    MultiSetNode *y = x->right;   // y是x的右子节点
    MultiSetNode *T2 = y->left;   // T2是y的左子节点
    y->left = x;                  // 将x作为y的左子节点
    x->right = T2;                // 将T2作为x的右子节点
    x->height = max(getHeight(x->left), getHeight(x->right)) + 1;  // 更新x的高度
    y->height = max(getHeight(y->left), getHeight(y->right)) + 1;  // 更新y的高度
    return y;                     // 返回新的根节点
}
// 工具函数:获取节点的平衡因子
int getBalance(MultiSetNode *node) {
    if (node == NULL) return 0;   // 空节点平衡因子为0
    return getHeight(node->left) - getHeight(node->right);  // 计算平衡因子
}
// 向多重集合中插入值
MultiSetNode* insertNode(MultiSetNode *node, int value, int *count_change, long long *sum_change) {
    if (node == NULL) {           // 如果节点为空,创建新节点
        *count_change = 1;        // 计数增加1
        *sum_change = value;      // 和增加value
        return createNode(value); // 返回新节点
    }
    if (value < node->value) {    // 如果值小于当前节点值,向左子树插入
        node->left = insertNode(node->left, value, count_change, sum_change);
    } else if (value > node->value) {  // 如果值大于当前节点值,向右子树插入
        node->right = insertNode(node->right, value, count_change, sum_change);
    } else {                      // 值等于当前节点值
        node->count++;            // 增加计数
        *count_change = 1;        // 计数增加1
        *sum_change = value;      // 和增加value
        return node;              // 返回当前节点
    }
    node->height = 1 + max(getHeight(node->left), getHeight(node->right));  // 更新节点高度
    int balance = getBalance(node);  // 获取平衡因子
    // 左左情况,需要右旋
    if (balance > 1 && value < node->left->value) {
        return rightRotate(node); // 返回右旋后的根节点
    }
    // 右右情况,需要左旋
    if (balance < -1 && value > node->right->value) {
        return leftRotate(node);  // 返回左旋后的根节点
    }
    // 左右情况,需要先左旋再右旋
    if (balance > 1 && value > node->left->value) {
        node->left = leftRotate(node->left);  // 对左子树左旋
        return rightRotate(node);              // 对当前节点右旋
    }
    // 右左情况,需要先右旋再左旋
    if (balance < -1 && value < node->right->value) {
        node->right = rightRotate(node->right);  // 对右子树右旋
        return leftRotate(node);                 // 对当前节点左旋
    }
    return node;                  // 返回当前节点
}
// 从多重集合中删除值
MultiSetNode* deleteNode(MultiSetNode *node, int value, int *count_change, long long *sum_change) {
    if (node == NULL) {           // 如果节点为空,返回NULL
        *count_change = 0;        // 计数无变化
        *sum_change = 0;          // 和无变化
        return node;              // 返回NULL
    }
    if (value < node->value) {    // 如果值小于当前节点值,向左子树删除
        node->left = deleteNode(node->left, value, count_change, sum_change);
    } else if (value > node->value) {  // 如果值大于当前节点值,向右子树删除
        node->right = deleteNode(node->right, value, count_change, sum_change);
    } else {                      // 找到要删除的节点
        if (node->count > 1) {    // 如果计数大于1,减少计数
            node->count--;        // 计数减1
            *count_change = -1;   // 计数减少1
            *sum_change = -value; // 和减少value
            return node;          // 返回当前节点
        } else {                  // 如果计数等于1,需要删除节点
            *count_change = -1;   // 计数减少1
            *sum_change = -value; // 和减少value
            if (node->left == NULL || node->right == NULL) {  // 如果有一个子节点为空
                MultiSetNode *temp = node->left ? node->left : node->right;  // 获取非空子节点
                if (temp == NULL) {  // 如果没有子节点
                    temp = node;     // 临时指针指向当前节点
                    node = NULL;     // 当前节点设为NULL
                } else {             // 有一个子节点
                    *node = *temp;   // 用子节点内容替换当前节点
                }
                free(temp);          // 释放要删除的节点内存
            } else {                 // 有两个子节点
                MultiSetNode *temp = node->right;  // 找到右子树的最小节点
                while (temp->left != NULL) {
                    temp = temp->left;
                }
                node->value = temp->value;  // 用最小节点的值替换当前节点值
                node->count = temp->count;  // 复制计数
                int dummy_count;     // 临时计数变量
                long long dummy_sum; // 临时和变量
                node->right = deleteNode(node->right, temp->value, &dummy_count, &dummy_sum);  // 删除右子树中的最小节点
            }
        }
    }
    if (node == NULL) return node;   // 如果节点为空,返回NULL
    node->height = 1 + max(getHeight(node->left), getHeight(node->right));  // 更新节点高度
    int balance = getBalance(node);  // 获取平衡因子
    // 左左情况,需要右旋
    if (balance > 1 && getBalance(node->left) >= 0) {
        return rightRotate(node);    // 返回右旋后的根节点
    }
    // 左右情况,需要先左旋再右旋
    if (balance > 1 && getBalance(node->left) < 0) {
        node->left = leftRotate(node->left);  // 对左子树左旋
        return rightRotate(node);              // 对当前节点右旋
    }
    // 右右情况,需要左旋
    if (balance < -1 && getBalance(node->right) <= 0) {
        return leftRotate(node);     // 返回左旋后的根节点
    }
    // 右左情况,需要先右旋再左旋
    if (balance < -1 && getBalance(node->right) > 0) {
        node->right = rightRotate(node->right);  // 对右子树右旋
        return leftRotate(node);                 // 对当前节点左旋
    }
    return node;                    // 返回当前节点
}
// 查找多重集合中的最大值
MultiSetNode* findMax(MultiSetNode *node) {
    if (node == NULL) return NULL;  // 空树返回NULL
    while (node->right != NULL) {   // 一直向右遍历
        node = node->right;
    }
    return node;                    // 返回最大节点
}
// 查找多重集合中的最小值
MultiSetNode* findMin(MultiSetNode *node) {
    if (node == NULL) return NULL;  // 空树返回NULL
    while (node->left != NULL) {    // 一直向左遍历
        node = node->left;
    }
    return node;                    // 返回最小节点
}
// 初始化多重集合
void initMultiSet(MultiSet *set) {
    set->root = NULL;               // 根节点初始为空
    set->total_count = 0;           // 总计数初始为0
    set->total_sum = 0;             // 总和初始为0
}
// 向多重集合添加元素
void addToMultiSet(MultiSet *set, int value) {
    int count_change;               // 计数变化量
    long long sum_change;           // 和变化量
    set->root = insertNode(set->root, value, &count_change, &sum_change);  // 插入节点
    set->total_count += count_change;  // 更新总计数
    set->total_sum += sum_change;   // 更新总和
}
// 从多重集合移除元素
void removeFromMultiSet(MultiSet *set, int value) {
    int count_change;               // 计数变化量
    long long sum_change;           // 和变化量
    set->root = deleteNode(set->root, value, &count_change, &sum_change);  // 删除节点
    set->total_count += count_change;  // 更新总计数
    set->total_sum += sum_change;   // 更新总和
}
// 获取多重集合中的最大值
int getMaxFromMultiSet(MultiSet *set) {
    MultiSetNode *maxNode = findMax(set->root);  // 查找最大节点
    if (maxNode == NULL) return INT_MIN;         // 空集合返回最小值
    return maxNode->value;          // 返回最大值
}
// 获取多重集合中的最小值
int getMinFromMultiSet(MultiSet *set) {
    MultiSetNode *minNode = findMin(set->root);  // 查找最小节点
    if (minNode == NULL) return INT_MAX;         // 空集合返回最大值
    return minNode->value;          // 返回最小值
}
// 获取多重集合的大小
int getMultiSetSize(MultiSet *set) {
    return set->total_count;        // 返回总计数
}
// 获取多重集合的和
long long getMultiSetSum(MultiSet *set) {
    return set->total_sum;          // 返回总和
}
// 创建MKAverage对象
MKAverage* mkAverageCreate(int m, int k) {
    MKAverage *obj = (MKAverage*)malloc(sizeof(MKAverage));  // 分配内存
    obj->m = m;                     // 设置窗口大小
    obj->k = k;                     // 设置排除数量
    obj->window = (int*)malloc(m * sizeof(int));  // 分配窗口数组内存
    obj->window_size = 0;           // 初始窗口大小为0
    obj->window_index = 0;          // 初始索引为0
    initMultiSet(&obj->small_set);  // 初始化小值集合
    initMultiSet(&obj->mid_set);    // 初始化中间值集合
    initMultiSet(&obj->large_set);  // 初始化大值集合
    return obj;                     // 返回创建的对象
}
// 向MKAverage添加元素
void mkAverageAddElement(MKAverage* obj, int num) {
    if (obj->window_size < obj->m) {  // 如果窗口未满
        obj->window[obj->window_index] = num;  // 直接添加元素
        obj->window_index = (obj->window_index + 1) % obj->m;  // 更新索引
        obj->window_size++;         // 窗口大小增加
    } else {                        // 如果窗口已满
        int remove_num = obj->window[obj->window_index];  // 获取要移除的元素
        obj->window[obj->window_index] = num;  // 用新元素替换旧元素
        obj->window_index = (obj->window_index + 1) % obj->m;  // 更新索引
        // 从对应的集合中移除旧元素
        if (getMultiSetSize(&obj->small_set) > 0 && remove_num <= getMaxFromMultiSet(&obj->small_set)) {
            removeFromMultiSet(&obj->small_set, remove_num);  // 从小值集合移除
        } else if (getMultiSetSize(&obj->large_set) > 0 && remove_num >= getMinFromMultiSet(&obj->large_set)) {
            removeFromMultiSet(&obj->large_set, remove_num);  // 从大值集合移除
        } else {
            removeFromMultiSet(&obj->mid_set, remove_num);    // 从中间集合移除
        }
    }
    // 添加新元素到中间集合
    addToMultiSet(&obj->mid_set, num);
    // 调整小值集合:如果小值集合大小小于k且中间集合不为空,将中间集合的最小值移到小值集合
    while (getMultiSetSize(&obj->small_set) < obj->k && getMultiSetSize(&obj->mid_set) > 0) {
        int min_mid = getMinFromMultiSet(&obj->mid_set);  // 获取中间集合最小值
        removeFromMultiSet(&obj->mid_set, min_mid);       // 从中间集合移除
        addToMultiSet(&obj->small_set, min_mid);          // 添加到小值集合
    }
    // 调整大值集合:如果大值集合大小小于k且中间集合不为空,将中间集合的最大值移到大值集合
    while (getMultiSetSize(&obj->large_set) < obj->k && getMultiSetSize(&obj->mid_set) > 0) {
        int max_mid = getMaxFromMultiSet(&obj->mid_set);  // 获取中间集合最大值
        removeFromMultiSet(&obj->mid_set, max_mid);       // 从中间集合移除
        addToMultiSet(&obj->large_set, max_mid);          // 添加到大值集合
    }
    // 平衡小值集合和中间集合
    while (getMultiSetSize(&obj->small_set) > 0 && getMultiSetSize(&obj->mid_set) > 0 &&
           getMaxFromMultiSet(&obj->small_set) > getMinFromMultiSet(&obj->mid_set)) {
        int max_small = getMaxFromMultiSet(&obj->small_set);  // 获取小值集合最大值
        int min_mid = getMinFromMultiSet(&obj->mid_set);      // 获取中间集合最小值
        removeFromMultiSet(&obj->small_set, max_small);       // 从小值集合移除最大值
        removeFromMultiSet(&obj->mid_set, min_mid);           // 从中间集合移除最小值
        addToMultiSet(&obj->small_set, min_mid);              // 将中间集合最小值添加到小值集合
        addToMultiSet(&obj->mid_set, max_small);              // 将小值集合最大值添加到中间集合
    }
    // 平衡中间集合和大值集合
    while (getMultiSetSize(&obj->mid_set) > 0 && getMultiSetSize(&obj->large_set) > 0 &&
           getMaxFromMultiSet(&obj->mid_set) > getMinFromMultiSet(&obj->large_set)) {
        int max_mid = getMaxFromMultiSet(&obj->mid_set);      // 获取中间集合最大值
        int min_large = getMinFromMultiSet(&obj->large_set);  // 获取大值集合最小值
        removeFromMultiSet(&obj->mid_set, max_mid);           // 从中间集合移除最大值
        removeFromMultiSet(&obj->large_set, min_large);       // 从大值集合移除最小值
        addToMultiSet(&obj->mid_set, min_large);              // 将大值集合最小值添加到中间集合
        addToMultiSet(&obj->large_set, max_mid);              // 将中间集合最大值添加到大值集合
    }
    // 最终调整:确保小值集合大小正好为k
    while (getMultiSetSize(&obj->small_set) > obj->k) {
        int max_small = getMaxFromMultiSet(&obj->small_set);  // 获取小值集合最大值
        removeFromMultiSet(&obj->small_set, max_small);       // 从小值集合移除
        addToMultiSet(&obj->mid_set, max_small);              // 添加到中间集合
    }
    // 最终调整:确保大值集合大小正好为k
    while (getMultiSetSize(&obj->large_set) > obj->k) {
        int min_large = getMinFromMultiSet(&obj->large_set);  // 获取大值集合最小值
        removeFromMultiSet(&obj->large_set, min_large);       // 从大值集合移除
        addToMultiSet(&obj->mid_set, min_large);              // 添加到中间集合
    }
}
// 计算MK平均值
int mkAverageCalculateMKAverage(MKAverage* obj) {
    if (obj->window_size < obj->m) {  // 如果窗口元素不足
        return -1;                   // 返回-1
    }
    long long mid_sum = getMultiSetSum(&obj->mid_set);  // 获取中间集合的和
    int mid_count = getMultiSetSize(&obj->mid_set);     // 获取中间集合的元素个数
    if (mid_count == 0) {            // 如果中间集合为空
        return 0;                    // 返回0
    }
    return (int)(mid_sum / mid_count);  // 计算平均值并向下取整
}
// 释放MKAverage对象内存
void mkAverageFree(MKAverage* obj) {
    // 注意:这里简化了多重集合节点的释放,实际应用中需要递归释放所有节点
    free(obj->window);               // 释放窗口数组内存
    free(obj);                       // 释放对象内存
}
// 演示函数
void demonstrateMKAverage() {
    printf("=== MK平均值算法演示 ===\n");
    // 创建MKAverage对象,窗口大小为3,排除1个最小和1个最大值
    MKAverage* obj = mkAverageCreate(3, 1);
    // 测试用例1:添加元素3
    printf("添加元素: 3\n");
    mkAverageAddElement(obj, 3);
    // 测试用例2:添加元素1
    printf("添加元素: 1\n");
    mkAverageAddElement(obj, 1);
    // 测试用例3:计算平均值(应该返回-1,因为元素不足3个)
    int result1 = mkAverageCalculateMKAverage(obj);
    printf("当前MK平均值: %d\n", result1);
    // 测试用例4:添加元素10
    printf("添加元素: 10\n");
    mkAverageAddElement(obj, 10);
    // 测试用例5:计算平均值(应该返回3)
    int result2 = mkAverageCalculateMKAverage(obj);
    printf("当前MK平均值: %d\n", result2);
    // 测试用例6:添加元素5
    printf("添加元素: 5\n");
    mkAverageAddElement(obj, 5);
    // 测试用例7:添加元素5
    printf("添加元素: 5\n");
    mkAverageAddElement(obj, 5);
    // 测试用例8:添加元素5
    printf("添加元素: 5\n");
    mkAverageAddElement(obj, 5);
    // 测试用例9:计算平均值(应该返回5)
    int result3 = mkAverageCalculateMKAverage(obj);
    printf("当前MK平均值: %d\n", result3);
    // 释放内存
    mkAverageFree(obj);
    printf("=== 演示结束 ===\n");
}
// 主函数
int main() {
    demonstrateMKAverage();          // 运行演示函数
    return 0;                        // 程序正常退出
}