剑指offer41. 数据流中的中位数 P214

剑指offer41. 数据流中的中位数 P214

题目:如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值

首先,题目说一个数据流,就证明可能不能一次读入内存(我们这里用一个数组模拟输入流)

如果将数组从中间一分为二,中间的一个(len是奇数)或者两个元素(len是偶数)左边都比他们小,右边都比他们大,前后两半部分不用排序,只用划分出来就行。那么最后在中间的就是中位数。
左半部分是最大堆,右边是最小堆。

这个想法是使用两个堆(一个最大堆,一个最小堆)保存输入数据。
max_pq是一个大堆,以较小的值保存数据的前半部分,而min_pq是最小堆,以较大的值保存数据的后半个。 每次插入新值时,我们都会先比较它是否小于max_pq的顶部(前一半的最大值),如果是,则将其插入max_pq。 否则,它属于下半部分。 插入后,我们必须平衡前半部分和后半部分,以确保它们具有相同的长度或长度差仅为1,否则最后两个堆顶的元素不在最中间。

priority_queue <int> max_pq; //最大堆,其中最大元素堆顶也小于最小堆中任一元素
priority_queue <int, vector<int>, greater<int> > min_pq;  // 最小堆,堆顶最小

bool isAnsValid = false;// 返回0.0时有效与否

void addHeap(int num) { // 插入节点
	if (max_pq.empty() || num < max_pq.top()) {    // 先插入最大堆
		max_pq.push(num);
	}
	else {                                // 当前值比最大堆顶还大,插入最小堆
		min_pq.push(num);
	}
	if (min_pq.size() > max_pq.size() + 1) {  // 最小堆元素个数比最大的超过1,调整
		int temp = min_pq.top();
		min_pq.pop();
		max_pq.push(temp);
	}
	else if (max_pq.size() > min_pq.size() + 1) {  // 同理
		int temp = max_pq.top();
		max_pq.pop();
		min_pq.push(temp);
	}
}
double findCore() {  // 找中位数
	if (!max_pq.empty()) {
		isAnsValid = true;                        // 返回值有效
		if (max_pq.size() == min_pq.size()) {        // len偶数,取均值
			return (max_pq.top() + min_pq.top()) / 2.0;
		} else {                               // len奇数,取堆顶
			return (max_pq.size() > min_pq.size()) ? max_pq.top() : min_pq.top();
		}
	}
	else 0.0; // 失败
}

double findMidInList(int *nums, int len) {
	if (nums == NULL || len < 1)
		return 0.0;	
	while (!max_pq.empty()) max_pq.pop();   // 清空堆
	while (!min_pq.empty()) min_pq.pop();
	for (int i = 0; i < len; ++i){      // 建堆
		addHeap(nums[i]);
	}
	return findCore();
}

扩展
优先队列(堆)
头文件:#include< queue >
大根堆定义:priority_queue< int >pq
小根堆定义:priority_queue< int ,vector< int >,greater< int > >pq
(注意最后两个“>”符号不要连在一起,否则很多编译器误认为是‘>>’运算符)

操作:
push() 元素入队 pop() 队首元素出队 top() 取队首元素
empty() 如果队列为空,则返回true(1),否则返回false(0)
size() 返回优先队列中拥有的元素个数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值