一、问题描述:
1、给定一系列数据,如果按大小排序,则中位数只是中间项(数据个数为奇数),或者
如果没有中间项(数据个数为偶数),则为两个中间项的平均值。 例如,序列 3, 1,
9, 25, 12 的中位数是 9 ,而序列 8, 4, 11, 18 的中位数是 9.5。
2、如果增加数据,则需要能够动态维护运行中位数。即当遇到每个新数据时,都会计
算更新的中位数。 当然,这应该尽可能高效地完成。
3、例如,依次读入数据 3, 1, 9, 25, 12,则动态返回的中位数列表应该是 3, 2.0, 3, 6.0, 9。
二、需求分析:
1. 首先设计一个计算动态中位数的初始算法版本,可以每次读入新数据时,及时排序
并更新当前中位数;
2. 设计计算动态中位数的高效优化算法版本,可借助于堆(heap)实现,并分析算法的
时间复杂度;
3. 对于高效优化版本,测试数据规模为对 10 万个随机数据计算中位数,统计运行时间
(单位:秒)
三、模块设计:
根据具体需求设计以下三个主要的模块:
(1)初始算法模块:初始算法的实现通过数组存储数据,进行插入排序,寻找中位数通过数组个数分类判断下标。
(2)高效优化版本模块:借助heap实现,利用大顶堆存小数,小顶堆存大数,利用堆插入保持大顶堆中的元素个数与小顶堆元素个数的相对平衡,寻找中位数可以直接取堆顶元素。
(3)对比测试模块:通过随机种子srand((unsigned int)time(0)) 生成随机数,计算高效版本和初始版本各自的执行时间,进行对比显示。对比测试设置两个函数分别测试两个版本的执行时间,可设置参数N为测试数据量的大小。
四、详细设计:
1、存储结构:
vector<double>v;//存放数据的数组
priority_queue<double>down;//大顶堆,存较小的一半
priority_queue<double,vector<double>,greater<double>> up;//小顶堆
2、主要算法设计:
(1)初始算法,插入x到数组并排序
void mysort(double x) {
v.push_back(x);
int n = v.size()-1;//之前的元素个数
if (n == 0)return;
int t=n+1;//从t号下标开始后移
for (int i = n - 1; i >= 0; --i) {//遍历找到x应当插入的位置
if (v[i] >= x) { t = i ; }
}
if (t == n+1)return;//已经有序
for (int i = n - 1; i >= t; --i) {//后移
v[i+1] = v[i];
}
v[t] = x;
}
double findm() {//寻找中位数(初始算法)
int n = v.size();
if (n % 2) {
return v[n / 2];
}
else {
double pj = (v[n / 2] + v[n / 2 - 1]) / 2;
return pj;
}
}
(2)高效算法,堆插入
void heapsort(double x) {
if (down.empty() || x <= down.top())down.push(x);
else up.push(x);
//调整
if (down.size() >= up.size() + 2) { up.push(down.top()); down.pop(); }
if (up.size() >= down.size() + 1){down.push(up.top()); up.pop();}
//始终保持down中个数=up/up+1
return;
}
void findheapm() { //寻找两堆的中位数
int num = down.size() + up.size();
if (num % 2)cout << "此时中位数为" << down.top() << endl;
else cout << "此时中位数为" << (down.top() + up.top()) / 2 << endl;
return;
}
(3)对比测试函数
void ff(int N) {//测试数据量N的堆算法
srand((unsigned int)time(0));//随机种子
clock_t start, end; double timee;//开始时间 结束时间 时间间隔
start = clock();
for (int i = 0; i < N; i++)heapsort(rand() % N);//生成随机数
end = clock();
timee = (double)(end - start) / CLOCKS_PER_SEC;//转化成秒
cout << "高效版本执行时间为:" << timee <<"s" << endl;
}
void ff1() {//测试初始算法
srand((unsigned int)time(0));//随机种子
clock_t start, end; double timee;//开始时间 结束时间 时间间隔
start = clock();
for (int i = 0; i < 10000; i++)mysort(rand() % 10000);//生成随机数
end = clock();
timee = (double)(end - start) / CLOCKS_PER_SEC;//转化成秒
cout << "初始版本执行时间为:" << timee << "s" << endl;
}
五、测试结果:
1) 测试用例设计:
初始版本输入3、1、2、3,返回动态中位数应为3、2、2、2.5;高效版本输入2、1、4、
7,返回动态中位数应为2、1.5、2、3。接下来两个测试功能:第一个测试初始版本和高效
版本数据规模为1万的用时结果,第二个单独测试高效版本对10万数据的用时。
2) 测试结果如下
感谢观看 欢迎交流讨论!!!