课程设计 随时找到数据流的中位数

题目

随时找到数据流中的中位数:有一个源源不断地吐出整数的数据流,假设你有足够的空间来保存吐出的数。请设计一个名叫MedianHolder的结构,MedianHolder可以随时取得之前吐出所有数的中位数。
要求:
1)如果MedianHolder已经保存了吐出的N个数,那么任意时刻将一个新数加入到MedianHolder的过程,其时间复杂度是O(logN)。
2)取得已经吐出的N个数整体的中位数的过程,时间复杂度为O(1)。

题意:
比如当前数据流式5 3 6 0 7 ,那么中位数字就是排序后的
0 3 5 6 7 的中位数5.

不能每次插入一个数据就排序,然后再取中位数字,因为这样是n*nlogn 的复杂度。nlogn是排序的最低的复杂度了。

思路:建立一个大根堆,一个小根堆, 定义一个数据插入规则, 比如当前数据个数是偶数的时候就把新数据插入到小根堆,当前数据个数是奇数的时候把数据插入到大根堆。
更重要的是大根堆的堆顶比小根堆的堆顶的数字要小

这样当数据的总数是奇数的时候,中位数字直接就是小根堆的“**堆顶”**而当数据的总数是偶数的时候中位数字是(大根堆堆顶 + 小根堆堆顶/2

前面说了当数据个数是偶数个的时候要把新数据插入到小根堆,如果
新数据比大根堆的堆顶还要小的话,那么就先把这个新的数据插入到大根堆,从大根堆的堆顶弹出一个数字,再把这个数字插入到小根堆,这样就保证了小根堆的数字都是大于等于大根堆的数字的!

同理当数据总数是奇数的时候要把新数据插入到大根堆,如果这个新的数据比小根堆的堆顶还要大的话,得把这个新数据插入到小根堆,然后调节小根堆,弹出小根堆的堆顶这个数字,再把这个数字压入到大根堆。这样做还是为了保证大根堆的数字是小于等于小根堆的数字的!


#include<iostream>
#include<climits>
#include<vector>
#include<map>
#include<cstdio>
#include<queue>
#include<string>
#include<cstring>
#include<stack>
#include<queue>
#include<cmath>
#include<set>
#include<algorithm>
using namespace std;



class MedianHolder{
public : 
	vector<int>smallHeap;//最小堆
	vector<int>bigHeap;//最大堆
	int lenOfsmallHeap;//最小堆数据个数
	int lenOfbigHeap;//最大堆数据个数

	MedianHolder(){
		lenOfsmallHeap = 0;
		lenOfbigHeap = 0;
	}

	void  solve(int num){//插入数据
		
		int length =  lenOfsmallHeap+ lenOfbigHeap;
		if(length & 1){//奇数个数的话插入最大堆
			if(lenOfsmallHeap > 0 && num > smallHeap[0]){
				
				insertsmallHeap(num);//插入最小堆并调整最小堆
				int tmp = smallHeap[0];//获取最小堆的堆顶
				movesmallVector();//弹出最小堆堆顶
				//弹出最小堆的堆顶后重新调整最小堆
				for(int i = lenOfsmallHeap / 2 - 1; i >= 0; i--)
					adjustsmallHeap(i);
				insertbigHeap(tmp);//插入最大堆并调整最大堆


			}
			else 
				insertbigHeap(num);
		}
		else{//偶数个数的话插入最小堆, 同上的做法
			if(lenOfbigHeap > 0 && num < bigHeap[0] ){
				
				insertbigHeap(num);
				int tmp = bigHeap[0];
				movebigVector();
				for(int i = lenOfbigHeap  / 2 - 1; i >= 0; i--)
					adjustbigHeap(i);
				insertsmallHeap(tmp);

			}
			else{
				insertsmallHeap(num);
			}
		}
			


	}

	double  getMedian(){//获取挡圈数据流的中位数字
		int length = lenOfsmallHeap + lenOfbigHeap;
		if(length == 0)
			return 0;
		if(length & 1)//数据流的数据个数是奇数个
			return smallHeap[0];
		else{//偶数个
			return (smallHeap[0] + bigHeap[0]) * 1.0 / 2.0;
		}
	}
	//调整最小堆
	void adjustsmallHeap(int index){
		int left = index * 2 + 1;
		int right = index * 2 + 2;
		int minIndex = index;
		if(left < lenOfsmallHeap && smallHeap[minIndex] > smallHeap[left])
			minIndex = left;

		if(right < lenOfsmallHeap && smallHeap[minIndex] > smallHeap[right])
			minIndex = right;

		if(minIndex != index){
			swap(smallHeap[index], smallHeap[minIndex]);//交换,把小的数字放上面
			adjustsmallHeap(minIndex);//递归调整
		}

		return ;

	}

	
	void adjustbigHeap(int index){//调整最大堆,同上最小堆

		int left = index * 2 + 1;
		int right = index * 2 + 2;
		int maxIndex = index;
		int lenOfbigHeap = bigHeap.size();
		if(left < lenOfbigHeap && bigHeap[maxIndex] < bigHeap[left])
			maxIndex = left;

		if(right < lenOfbigHeap && bigHeap[maxIndex] < bigHeap[right])
			maxIndex = right;


		if(maxIndex != index){
			swap(bigHeap[index], bigHeap[maxIndex]);
			adjustbigHeap(maxIndex);
		}

		return ;

	}

	void insertsmallHeap(int num){//插入数据到最小堆并调节最小堆
		smallHeap.push_back(num);
		lenOfsmallHeap++;//堆长度增加
		
		for(int i = lenOfsmallHeap / 2 - 1; i >= 0; i--)
			adjustsmallHeap(i);//从最后一个非叶子节点开始调节最小堆
	}

	void insertbigHeap(int num){//插入数据到最大堆并调节最大堆
		//同上最小堆
		bigHeap.push_back(num);
		lenOfbigHeap++;
		
		for(int i = lenOfbigHeap / 2 - 1; i >= 0; i--)
			adjustbigHeap(i);
	}

	//弹出最小堆的堆顶
	void movesmallVector(){
		//把1号位置下方的元素向前移动一位,相当于弹出堆顶。
		for(int i = 1; i < lenOfsmallHeap; i++)
			smallHeap[i - 1] = smallHeap[i];
		smallHeap.pop_back();//弹出了堆顶,前面元素整体左移了一位, 少一个元素,尾部不要了
		lenOfsmallHeap--;//堆长度减少
	}
	//弹出最大堆的堆顶,同上最小堆
	void movebigVector(){

		for(int i = 1; i < lenOfbigHeap; i++)
			bigHeap[i - 1] = bigHeap[i];
		bigHeap.pop_back();
		lenOfbigHeap--;
	}

	
};



int main()
{
	MedianHolder Mobj;
	vector<int>test;
	int x;
	while(true){
		cout<<"输入加入的数据: ";
		cin>>x;
		test.push_back(x);
		Mobj.solve(x);
		cout<<"当前数据序列是 ";
		for(int i = 0; i < test.size(); i++)
			cout<<test[i]<<" ";
		cout<<" 当前序列的中位数是: "<<Mobj.getMedian()<<endl;


	}
	
	
	system("pause");
}

测试一:
在这里插入图片描述

测试二:无序的数据:
在这里插入图片描述

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值