程序员面试金典——解题总结: 9.18高难度题 18.9随机生成一些数字并传入某个方法。编写一个程序,每当收到新数字时,找出并记录中位数。

#include <iostream>
#include <stdio.h>
#include <set>
#include <vector>
#include <algorithm>

using namespace std;
/*
问题:随机生成一些数字并传入某个方法。编写一个程序,每当收到新数字时,找出并记录中位数。
分析:题目的意思应该是:根据随机生成了k个数字之后,接下来每次再收到新数字时,找到中位数。
      简化问题:寻找中位数,什么是中位数?就是数组A排序之后,假设共有n个数字,
	  如果n为奇数,那么A[n/2]即为中位数
	  如果n为偶数,那么A[n/2 - 1] , A[n/2 - 1]累加之后除以2。假设我事先对之前的随机生成的元素组成的数组A排序,
	  不妨假设不管n为奇数或者偶数,寻找的中位数都是A[n/2],那么我可以事先取出中间3个数A[n/2 - 1],A[n/2],A[n/2+1]
	  然后用新生成的数字和A[n/2]比较,
	  如果A[n/2] > 生成的数字,那么新的中位数直接就是A[n/2 - 1]
	  如果A[n/2] < 生成的数字,                      A[n/2 + 1]
	             =                                   A[n/2]

书上解法:
用两个优先级堆,大顶堆:存放小于中位数的值,小顶堆:存放大于中位数的值。则大顶堆的对顶是最大值,小顶堆的对顶是最小值。
如果:大顶堆的大小 > 小顶堆的元素大小,则大顶堆的堆顶为中位数
                  =                 ,则大小顶堆的平均值为中位数。
只需要使得大顶堆的大小>=小顶堆的大小。
有新元素生成,如果 新元素 <= 中位数,放入大顶堆(即包含较小元素的那一堆);
                          >              小
如果插入新元素后,大顶堆元素个数 >= 小顶堆元素个数+2,说明较小的那一堆元素个数多了,将大顶堆的对顶移动到小顶堆
                  大顶堆元素个数 < 小顶堆元素个数,将小顶堆的对顶移动到大顶堆

输入:
4(初始元素个数)
1 4 7 10
3(后续生成的新元素个数)
3 2 8
输出:
4.00(后续第一个新元素加入后的中位数,保留两位小数) 
3.50
4.00

关键:
1
用两个优先级堆,大顶堆:存放小于中位数的值,小顶堆:存放大于中位数的值。则大顶堆的对顶是最大值,小顶堆的对顶是最小值。
如果:大顶堆的大小 > 小顶堆的元素大小,则大顶堆的堆顶为中位数
                  =                 ,则大小顶堆的平均值为中位数。
只需要使得大顶堆的大小>=小顶堆的大小。
有新元素生成,如果 新元素 <= 中位数,放入大顶堆(即包含较小元素的那一堆);
                          >              小
如果插入新元素后,大顶堆元素个数 >= 小顶堆元素个数+2,说明较小的那一堆元素个数多了,将大顶堆的对顶移动到小顶堆
                  大顶堆元素个数 < 小顶堆元素个数,将小顶堆的对顶移动到大顶堆
*/
class CompareMax
{
public:
	bool operator()(const int a ,const int b)const
	{
		return a > b ? true : false;
	}
};

class CompareMin
{
public:
	bool operator()(const int a ,const int b)const
	{
		return a < b ? true : false;
	}
};

//利用初始的数组产生两个堆,大顶堆存放较小部分元素,小顶堆存放较大部分元素,且满足: 小顶堆元素个数<= 大顶堆元素个数 <= 小顶堆元素个数+1
void generateSet(vector<int>& datas , multiset<int , CompareMax >& maxHeap , multiset<int , CompareMin >& minHeap )
{
	if(datas.empty())
	{
		return;
	}
	//初始先排序
	sort(datas.begin() , datas.end());
	int size = datas.size();
	int middleNum = (size + 1) / 2;
	//将前半段小的放入到大顶堆中
	for(int i = 0 ; i < size ; i++)
	{
		int value = datas.at(i);
		if(i < middleNum)
		{
			maxHeap.insert(value);
		}
		else
		{
			minHeap.insert(value);
		}
	}
}

double getMiddleNum(multiset<int , CompareMax >& maxHeap , multiset<int , CompareMin >& minHeap )
{
	int minSize = maxHeap.size();
	int maxSize = minHeap.size();
	double originalMiddleNum = 0;
	//计算原始中位数,如果总元素个数是奇数个,那么原始中位数等于大顶堆的堆顶
	if( ( (minSize + maxSize) & 1 ) == 1)
	{
		originalMiddleNum = *(maxHeap.begin());
	}
	else
	{
		originalMiddleNum = ( *maxHeap.begin() + *minHeap.begin() ) / 2.0;
	}
	return originalMiddleNum;
}

//根据输入的新元素,大顶堆和小顶堆,生成中位数
double generateMiddleNum(int value, multiset<int , CompareMax >& maxHeap , multiset<int , CompareMin >& minHeap )
{
	double originalMiddleNum = getMiddleNum(maxHeap , minHeap);
	//比较如果:新元素 <= 原始中位数,则插入到大顶堆(即较小部分的那一堆元素中)
	if( 1.0*value <= originalMiddleNum )
	{
		maxHeap.insert(value);
	}
	else
	{
		minHeap.insert(value);
	}
	//如果大顶堆元素个数 >= 小顶堆元素个数 + 2 ,就把大顶堆的堆顶移除,并插入到小顶堆中
	int minSize = maxHeap.size();
	int maxSize = minHeap.size();
	if( minSize >= maxSize + 2)
	{

		int value = *maxHeap.begin();
		maxHeap.erase(maxHeap.begin());
		minHeap.insert(value);
	}
	//如果大顶堆元素个数 < 小顶堆元素个数,就把小顶堆的堆顶移除,并插入到大顶堆中
	else if(minSize < maxSize)
	{
		int value = *minHeap.begin();
		minHeap.erase(minHeap.begin());
		maxHeap.insert(value);
	}
	originalMiddleNum = getMiddleNum(maxHeap , minHeap);
	return originalMiddleNum;
}

void process()
{
	int originalNum , addNum ;
	vector<int> datas;
	vector<int> newDatas;
	int value;
	multiset<int , CompareMax > maxHeap;
	multiset<int , CompareMin > minHeap;
	double middleNum;
	cout.setf(ios::fixed);
	cout.precision(2);
	while(cin >> originalNum )
	{
		datas.clear();
		newDatas.clear();
		maxHeap.clear();
		minHeap.clear();
		for(int i = 0 ; i < originalNum ; i++)
		{
			cin >> value;
			datas.push_back(value);
		}
		//生成初始的大顶堆和小顶堆
		generateSet(datas , maxHeap , minHeap);
		cin >> addNum;
		for(int i = 0 ; i < addNum ; i++)
		{
			cin >> value;
			//接下来对每次新输入的元素,生成中位数
			middleNum = generateMiddleNum(value , maxHeap , minHeap);
			cout << middleNum << endl;
		}
	}
}

int main(int argc , char* argv[])
{
	process();
	getchar();
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值