C++ 利用最大和最小堆快速找到中位数【优先级队列】

寻找中位数

题目

设计一个数据结构,该数据结构动态维护一组数据,且支持如下操作:
1.添加元素:void addNum(int num),将整型num添加至数据结构中。
2.返回数据的中位数,double findMedian(),返回其维护的数据的中位数
中位数定义:
1.若数据个数为奇数,中位数是该组数排序后中间的数。[1,2,3]->2
2.若数据个数为偶数,中位数是该组数排序后中间的两个数字的平均值。[1,2,3,4]->2.5

思路

较为普遍的思想都是按数据元素个数的多少,分为奇偶两种情况去讨论:
当为奇数时,找到下标为(元素个数 / 2)位置的数即为中位数。
当为偶数时,找到下标为((元素个数/2)的数+(元素个数/2-1)的数)/2为中位数。

但如果是将这一组数据遍历式的寻找到中位数的值,那么搜索时间的复杂度就会是O(n^2),那我们又应该如何降低时间复杂度呢?

这就运用到了最大及最小堆的方法:
同时建立最大堆和最小堆,将需要放入的元素进行比较后再放入,最终将近一半较大的数放入到最小堆中,将另一半较小的数放入到最大堆中,最后比较两堆的元素个数,较多的一方取其堆顶元素作为中位数,若相等,取两堆的(堆顶元素之和)/ 2[注:这里的中位数要是浮点型]

在以下几种情况下将元素放入到这两个堆中:
①当最大堆中的元素个数为0(即为)时,将元素放入其中。(即将第一个元素放入到最大堆)
②当big_heap.size() == small_heap.size()时,比较最大堆的堆顶元素与将放入的元素大小,如果小于其堆顶的元素,则直接放入到最大堆中,否则放入到最小堆中。
③当big_heap.size() > small_heap.size()时,还是将放入的元素与最大堆的堆顶元素进行比较,如果大于堆顶元素,直接放入最小堆,否则,将最大堆的堆顶元素push到最小堆中,再将其弹出最大堆,最后将要放入的元素放入到最大堆中。
④当big_heap.size() < small_heap.size()时,则比较最小堆的堆顶元素与放入元素的大小,如果小于最小堆的堆顶元素,则直接放入到最大堆中,否则,将最小堆的堆顶元素放入到最大堆中,再将堆顶元素弹出,将要放入的元素放入到最小堆中。

如图所示:




代码如下:

#include <stdio.h>
#include <queue>
class MedianFinder {
public:
    MedianFinder() {
    }
    void addNum(int num) {
    	if (big_queue.empty()){
	    	big_queue.push(num);
	    	return;
	    }
        if (big_queue.size() == small_queue.size()){
        	if (num < big_queue.top()){
	        	big_queue.push(num);
	        }
	        else{
        		small_queue.push(num);
        	}
        }
        else if(big_queue.size() > small_queue.size()){
        	if (num > big_queue.top()){
	        	small_queue.push(num);
	        }
	        else{
        		small_queue.push(big_queue.top());
        		big_queue.pop();
        		big_queue.push(num);
        	}
        }
        else if(big_queue.size() < small_queue.size()){
        	if (num < small_queue.top()){
	        	big_queue.push(num);
	        }
	        else{
        		big_queue.push(small_queue.top());
        		small_queue.pop();
        		small_queue.push(num);
        	}
        }
    }
    double findMedian(){
    	if (big_queue.size() == small_queue.size()){
        	return (big_queue.top() + small_queue.top()) / 2;
        }
        else if (big_queue.size() > small_queue.size()){
        	return big_queue.top();
        }
        return small_queue.top();
    }
private:
	std::priority_queue<double> big_queue;
	std::priority_queue<double, std::vector<double>,
					std::greater<double> > small_queue;
};
int main(){
	MedianFinder M;
	int test[] = {6, 10, 1, 7, 99, 4, 33};
	for (int i = 0; i < 7; i++){
		M.addNum(test[i]);
		printf("%lf\n", M.findMedian());
	}
	return 0;
}

致谢

本章知识点和思路由小象学院相关视频提供,由本人学习并梳理得出,希望自己加深记忆的同时,也能给大家提供更多有关于一些算法的知识点。
你的点赞、评论、收藏就是对我最大的支持与鼓励,谢谢!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值