在金融市场的实时交易中,分析师需要剔除极端报价后计算公平价格;在社交媒体的热门内容筛选中,平台需要过滤掉最高和最低的极端评分。这些场景都在重复一个数学问题:如何从流动的数据中,动态地排除极端值后计算有意义的平均值?
今天,让我们一同探索这个看似简单却蕴含深意的算法挑战——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时完全无法接受。
多数据结构协同策略:
最优解法需要多种数据结构的精妙配合:
滑动窗口维护:使用队列(deque)维护最近m个元素,保证FIFO特性
元素分类管理:将窗口内元素分为三个有序集合:
最小k个元素集合
中间m-2k个元素集合
最大k个元素集合
平衡数据结构选择:使用平衡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:
输入:
["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; // 程序正常退出
}
浙公网安备 33010602011771号