题目:如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值
首先,题目说一个数据流,就证明可能不能一次读入内存(我们这里用一个数组模拟输入流)
如果将数组从中间一分为二,中间的一个(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() 返回优先队列中拥有的元素个数