左神算法初级班3中运用堆结构求解的那个问题 题解

题目描述:

有一个容器,一次吐出数字,让你求给定任意时刻所吐出来的数的中位数

基本思路:

分别建立一个大根堆和一个小根堆,用来维护排序后的数组小于中间数字的数(放入大根堆中)和大于中间数字的数(放入小根堆中),当大根堆和小根堆中数据个数相差大于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;
    }

}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值