题目要求:

Download the text file here.

The goal of this problem is to implement the "Median Maintenance" algorithm (covered in the Week 5 lecture on heap applications). The text file contains a list of the integers from 1 to 10000 in unsorted order; you should treat this as a stream of numbers, arriving one by one. Letting xidenote the ith number of the file, the kth median mk is defined as the median of the numbers x1,…,xk. (So, if k is odd, then mk is ((k+1)/2)th smallest number among x1,…,xk; if k is even, then mk is the (k/2)th smallest number among x1,…,xk.)

In the box below you should type the sum of these 10000 medians, modulo 10000 (i.e., only the last 4 digits). That is, you should compute (m1+m2+m3+⋯+m10000) mod 10000.

OPTIONAL EXERCISE: Compare the performance achieved by heap-based and search-tree-based implementations of the algorithm.

大致意思是说,有一个文件,其中包含了1~10000这一万无序的数字,要求我们每次读入一个数字,并且每次读入数字后,找出所有已读入数字的中位数,计算所有这些中位数的和,然后输出和模10000的结果。

文件中的数据差不多是这样子的:

...
6195
2303
5685
1354
4292
7600
6447
4479
9046
7293
5147
1260
1386
6193
4135
3611
8583
...

解题思路:

这道题当然可以采用最暴力的方法,即每次读入一个数后就对数组进行排序,然后记录中位数,但是显然应该还有更好的方法。没错,如果借用“堆”这一数据结构,可以让算法的时间复杂度大大降低。具体的思路如下:

  • 创建两个堆:最大堆和最小堆(最大堆即父节点大于子节点的堆,反之则是最小堆);
  • 每次读入一个数后,我们将它和最大堆与最小堆的根节点大小进行比较,如果大于最大堆的根节点,那么就把它插入到最小堆当中;反之,就插入最大堆当中。可以想象一下,通过这个操作,比这两个根节点大的数字都在最小堆的根节点之下,而比这两个根节点小的数字,都在最大堆的根节点之下;
  • 有了上述结论后,我们还不能保证中位数就在两个根节点中,因为两个堆的大小可能会差的很大,因此每次读入一个数并且插入相应的堆后,我们都要检查两个堆的大小,然后平衡他们的大小(只有在两个堆的大小差异不大于1的情况下, 中位数才是两个根节点中的一个)
  • 平衡的具体做法是:如果两个堆的大小差异超过了1,那么就把size较大的那个堆的根节点pop出来,并将其插入到size较小的堆中;
  • 最后就是计算中位数了,因为最小堆的根节点会大于最大堆的根节点,因此如果最小堆的size比最大堆大1,那么中位数就是最小堆根节点;如果两者大小相等,或者最大堆的size比最小堆大1,那么中位数就是最大堆的根节点。

代码实现:

有了上述的思路,利用C++对其进行了实现,代码如下:

#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <limits>
using namespace std; class MinMaxHeap {
public:
MinMaxHeap(bool is_min);
~MinMaxHeap();
int Top();
int Size();
void Insert(int num);
void Pop();
private:
void swap(int index1, int index2);
int size;
int *element;
bool is_min;
}; MinMaxHeap::MinMaxHeap(bool is_min = true) {
// for this problem, 5010 is just fine
this->element = new int[];
this->size = ;
this->is_min = is_min;
} MinMaxHeap::~MinMaxHeap() {
delete[] this->element;
} int MinMaxHeap::Top() {
return this->element[];
} int MinMaxHeap::Size() {
return this->size;
} // The position of each element(the number means the index of the array)
// 0
// / \
// 1 2
// / \ / \
// 3 4 5 6 void MinMaxHeap::Insert(int num) {
int pos = size;
element[size++] = num;
if (is_min) {
while (pos > ) {
int parent = (pos - ) >> ; // same as (pos - 1) / 2
if (element[parent] <= element[pos]) {
break;
}
swap(parent, pos);
pos = parent;
}
}
else {
while (pos > ) {
int parent = (pos - ) >> ; // same as (pos - 1) / 2
if (element[parent] >= element[pos]) {
break;
}
swap(parent, pos);
pos = parent;
}
}
} void MinMaxHeap::Pop() {
element[] = element[--size];
int pos = ; if (is_min) {
while (pos < (size >> )) // if pos >= (size / 2), then element[pos] must be a leaf
{
int left_child = pos * + ;
int right_child = left_child + ;
int smallest_child; if (right_child < size && element[left_child] > element[right_child]) {
smallest_child = right_child;
}
else {
smallest_child = left_child;
} if (element[pos] < element[smallest_child]) {
break;
} swap(pos, smallest_child);
pos = smallest_child;
}
}
else {
while (pos < (size >> )) // if pos >= (size / 2), then element[pos] must be a leaf
{
int left_child = pos * + ;
int right_child = left_child + ;
int biggest_child; if (right_child < size && element[left_child] < element[right_child]) {
biggest_child = right_child;
}
else {
biggest_child = left_child;
} if (element[pos] > element[biggest_child]) {
break;
} swap(pos, biggest_child);
pos = biggest_child;
}
}
} void MinMaxHeap::swap(int index1, int index2) {
int tmp = element[index1];
element[index1] = element[index2];
element[index2] = tmp;
} int main() {
ifstream fin;
fin.open("Median.txt"); MinMaxHeap MinHeap(true);
MinMaxHeap MaxHeap(false);
// because we want to find the median, so insert
// both min of int and max of int is ok.
MinHeap.Insert(numeric_limits<int>::max());
MaxHeap.Insert(numeric_limits<int>::min()); int input, sum = , min_top, max_top;
string tmp;
while (getline(fin, tmp)) {
input = atoi(tmp.c_str());
min_top = MinHeap.Top();
max_top = MaxHeap.Top();
if (input < max_top) {
MaxHeap.Insert(input);
}
else {
MinHeap.Insert(input);
}
// balance
if (MaxHeap.Size() > MinHeap.Size() + ) {
max_top = MaxHeap.Top();
MaxHeap.Pop();
MinHeap.Insert(max_top);
}
if (MinHeap.Size() > MaxHeap.Size() + ) {
min_top = MinHeap.Top();
MinHeap.Pop();
MaxHeap.Insert(min_top);
}
//find the median
if (MinHeap.Size() == MaxHeap.Size() + ) {
sum += MinHeap.Top();
}
else {
sum += MaxHeap.Top();
}
} cout << sum % << endl;
fin.close();
system("pause");
return ;
}

通过这个方法,运算效率大大提升。

一个"Median Maintenance"问题的更多相关文章

  1. [Algorithm] Median Maintenance algorithm implementation using TypeScript / JavaScript

    The median maintenance problem is a common programming challenge presented in software engineering j ...

  2. Algorithms Part 1-Question 6- 2SUM Median-数和以及中位数问题

    本次有两个编程问题,一个是求两个数的和满足一定值的数目,另一个是求中位数. 2SUM问题 问题描述 The goal of this problem is to implement a variant ...

  3. 18个有用的 .htaccess 文件使用技巧

    .htaccess 是 Web 服务器 Apache 中特有的一个配置文件,操控着服务器上的许多行为,我们可以利用它来做许多事情,例如:设置访问权限,网址重定向,等等.本文向大家展示18条 .htac ...

  4. Categories  VS Extensions (分类 vs 扩展)

    转载翻译自:http://rypress.com/tutorials/objective-c/categories 一.Categories(分类)      Categories是一个把单个类定义分 ...

  5. corosync+pacemaker的crmsh的常用指令介绍

    配置crmsh的yum仓库,此仓库的RPM包有openSUSE提供,将这个network:ha-clustering:Stable.repo文件直接下载到本地并且命名为crmsh.repo wget ...

  6. 自学Zabbix3.12.5-动作Action-Condition配置

    点击返回:自学Zabbix之路 点击返回:自学Zabbix4.0之路 点击返回:自学zabbix集锦 3.12.5 自学Zabbix3.12.5-动作Action-Condition配置 报警,肯定是 ...

  7. GIT团队合作探讨之四--不同工作流优缺辨析

    由于git非常强大,它可以支持非常多的协作模式,而可能正因为选择太多反而有时候对于我们如何开始开展团队协作无从下手.本文试图阐述企业团队中应用最为广泛的git 工作流,为大家理清思路,最大限度发挥gi ...

  8. iDempiere 使用指南 插件安装过程

    Created by 蓝色布鲁斯,QQ32876341,blog http://www.cnblogs.com/zzyan/ iDempiere官方中文wiki主页 http://wiki.idemp ...

  9. 中位数II

    该题目与思路分析来自九章算法的文章,仅仅是自己做个笔记! 题目:数字是不断进入数组的,在每次添加一个新的数进入数组的同时返回当前新数组的中位数. 解答: 这道题是用堆解决的问题.用两个堆,max he ...

随机推荐

  1. JS中this到底指向谁?

    关于this的指向,是一个令人很头疼的问题.但是,你运气好,碰到了我.老夫这儿有本祖传秘籍,看懂这个,妈妈再也不用担心你的this指向不对啦! 归根结底,this指向就一句话:谁最终调用函数,this ...

  2. robotframe 学习笔记(之一)

    在robot framework中,通过 Set variable关键字来定义变量 连接对象: 通过Catenate关键字可以连接多个信息 加上"SEPARATOR=",可以对多个 ...

  3. java加密解密研究6、MD算法家族

    一.简述 MD5算法是典型的消息摘要算法,其前身有MD2.MD3和MD4算法,它由MD4.MD3和MD2算法改进而来.不论是哪一种MD算法,它们都需 要获得一个随机长度的信息并产生一个123位的信息摘 ...

  4. react router 4.0以上的路由应用

    thead>tr>th{padding:8px;line-height:1.4285714;border-top:1px solid #ddd}.table>thead>tr& ...

  5. Zxing 的集成 ---- Maven 对应 Gradle 的写法

    Zxing 的集成 ---- Maven 对应 Gradle 的写法 刚刚想耍耍二维码,想到了zxing和zbar,又想到zxing是Google老爹的,想想就算了吧,虽然zbar快但是识别错误率也高 ...

  6. React 读书笔记

    序言: 领导安排部门同事本月内看一本跟自己职业相关的书籍, 根基类的书籍已经看过了,重复阅读的意义不大,所以我平时看的都是视频,也许是视频作者没有出书的条件,也许是现在出书看的人越来越少了,也许有其他 ...

  7. redis集群搭建实践

    参考 第一个节点 第一个节点为本地的机器 IP:192.168.23.148 检查机器配置 $ uname -a Linux wangya-Lenovo-G480 4.8.0-52-generic # ...

  8. {网络编程}和{多线程}应用:基于TCP协议【实现多个客户端发送文件给一个服务器端】--练习

    要求: 实现多个客户端发送文件给一个服务器端 提示:多个人创建客户端发送文件,服务端循环接收socket,从socket中获取文件 说明:这里我们只要建立一个服务端就可以了,然后让多台电脑使用客户端给 ...

  9. QT调用百度语音REST API实现语音合成

    QT调用百度语音REST API实现语音合成 1.首先点击点击链接http://yuyin.baidu.com/docs/tts 点击access_token,获取access_token,里面有详细 ...

  10. C++经典绘图工具EasyX

    EasyX简介 EasyX 在学习C语言时,很多同学抱怨说C只能写最简单的Demo程序,通过printf在屏幕上打印字符来验证代码.这样的编程很枯燥,一点没觉得自己在设计软件. EasyX是针对C++ ...