题目描述:
有一个容器,一次吐出数字,让你求给定任意时刻所吐出来的数的中位数
基本思路:
分别建立一个大根堆和一个小根堆,用来维护排序后的数组小于中间数字的数(放入大根堆中)和大于中间数字的数(放入小根堆中),当大根堆和小根堆中数据个数相差大于1时,就让数据多的那个堆中的数跑到数据少中的那个堆中去,这样能够保证两个堆的堆顶的数始终是排序后数组最中间的两个数。为啥2个堆顶的数能够是排序后数组最中间的两个数呢。这是因为大根堆和小根堆的性质决定的。大根堆(堆中堆顶的数是整个堆中最大的数),小根堆(堆中堆顶的数是整个堆中最小的数)。如果把大根堆放在前面,小根堆放在后面,这两个堆拼接起来,正好能够从中间分出一道界限,左边的是大根堆里的数,都小于中间的两个数中左边的数,右边是小跟堆里的数,都大于中间两个数中右边的数。整体是有序的,但是局部还是不有序,但是没有关系,我们要的只是分界点的那两个数字。
代码如下:
#include<bits/stdc++.h>
using namespace std;
int a;//每次流出的数
int maxheap[9999999];//大根堆数组
int minheap[9999999];//小根堆数组
int maxheapsize=0;//大根堆当前状态的大小
int minheapsize=0;//小根堆当前状态的大小
int n;//表示哪个时刻求中位数,相当于数组的大小
void maxswap(int i,int j)//交换大根堆数组中i,j位置上的数
{
int temp=maxheap[i];
maxheap[i]=maxheap[j];
maxheap[j]=temp;
}
void minswap(int i,int j)//交换小根堆数组中i,j位置上的数
{
int temp=minheap[i];
minheap[i]=minheap[j];
minheap[j]=temp;
}
void maxheapinsert(int i)//加大根堆操作
{
int index=i;
while (maxheap[index]>maxheap[(index-1)/2])
{
maxswap(index,(index-1)/2);
index=(index-1)/2;
}
}
void minheapinsert(int i)//加小根堆操作
{
int index=i;
while (minheap[index]<minheap[(index-1)/2])
{
minswap(index,(index-1)/2);
index=(index-1)/2;
}
}
void maxheapfy(int i,int maxheapsize)//大根堆数变小后再次调成大根堆,i代表哪个位置上的数变化了,maxheapsize代表大根堆数组的大小
{
int xmax=i*2+1;
while (xmax<maxheapsize)
{
xmax=maxheap[xmax+1]>maxheap[xmax]&&(xmax+1<=maxheapsize)?xmax+1:xmax;
xmax=maxheap[i]>maxheap[xmax]?i:xmax;
if(xmax==i)
break ;
maxswap(xmax,i);
i=xmax;
xmax=i*2+1;
}
}
void minheapfy(int i,int minheapsize)//小根堆数变大后再次调成小根堆,i代表哪个位置上的数变化了,maxheapsize代表小根堆数组的大小
{
int xmin=i*2+1;
while (xmin<minheapsize)
{
xmin=minheap[xmin+1]<minheap[xmin]&&(xmin+1<=minheapsize)?xmin+1:xmin;
xmin=minheap[xmin]<minheap[i]?xmin:i;
if(xmin==i)
break ;
minswap(i,xmin);
i=xmin;
xmin=i*2+1;
}
}
int main()
{
while (cin>>n)
{
for(int i=0;i<n;i++)
{
cin>>a;
if(i==0)//当流出第一个数的时候,这个数必定进大根堆
{
maxheap[maxheapsize]=a;
maxheapsize++;
continue ;
}
if(a<=maxheap[0])//当流出的数a小于大根堆堆顶的数的时候,数a进入大根堆
{
maxheap[maxheapsize]=a;
maxheapinsert(maxheapsize);
maxheapsize++;
}
else//当流出的数a大于大根堆堆顶的数的时候,数a进入小根堆
{
minheap[minheapsize]=a;
minheapinsert(minheapsize);
minheapsize++;
}
if(maxheapsize-minheapsize>1)//当大根堆和小根堆中的数相差大于1个的时候,哪个堆的数多就把哪个堆的堆顶的数放入到数少的堆中。这里是大根堆中的数多,具体咋换,看下面
{
minheap[minheapsize]=minheap[0];//先把小根堆中堆顶的元素移动到堆底的最后一个再加一个的位置
minheapsize++;//此时小根堆数字的个数加1;
minheap[0]=maxheap[0];//把大根堆中堆顶的数移动到小根堆的堆顶
maxheap[0]=maxheap[maxheapsize-1];//把大根堆中最后一个元素移动到大根堆的堆顶
maxheapsize--;//大根堆数字的个数-1
minheapinsert(minheapsize-1);//然后对小根堆进行heapinsert操作重新调整为小根堆
maxheapfy(0,maxheapsize);//对大根堆进行heapfy操作,重新调整成为大根堆
}
if(minheapsize-maxheapsize>1)
{
maxheap[maxheapsize]=maxheap[0];//和上面的一样
maxheapsize++;
maxheap[0]=minheap[0];
minheap[0]=minheap[minheapsize-1];
minheapsize--;
maxheapinsert(maxheapsize-1);
minheapfy(0,minheapsize);
}
}
double jieguo=((minheap[0]+maxheap[0])*1.0)/2;
cout<<jieguo<<endl;
}
}