基础数据结构——二叉堆

二叉堆是一种基础的数据结构,它的操作相较于其他的数据结构(如栈,队列)操作较为有限,一般为插入,删除,查询等。

二叉堆的性质

堆是什么?

  1. 它是一种特殊的完全二叉树。
  2. 根节点为优先度最大的数据,根节点的子节点优先度稍微低于根节点,以此类推,越往下优先度越低。优先度不一定为数学意义上的大小,根据实际情况选择。
  3. 基础的有两种堆,根节点值最大的堆称为大根堆,反过来,根节点最小的称为小根堆。

堆的操作

建立堆

堆因为是一种特殊的完全二叉树,我们为了简便,通常可以用数组存储。

for(i=0;i<num;i++)
        scanf("%d",&h[i]);

此时数组中是杂乱无章的数,此时我们需要通过向上或向下比较来使堆成为一个小根堆或大根堆。

向下调整函数
void siftdown(int i)//传入一个需要调整的节点编号
{
    int flag=0,t;//flag用来标记是否需要调整
    //当i至少有左儿子,并且需要调整时,执行循环
    while(i*2<n&&flag==0)
    {
    	//与左儿子进行比较,记录小的数的下标
        if(h[i]<h[2*i])
            t=i;
        else
            t=2*i;
        //如果有右儿子,再将上面比较过的小的数与右儿子进行比较
        if(i*2+1<n)
        {
            if(h[t]>h[i*2+1])
            t=2*i+1;
        }
        //如果t!=i,我们就交换t与i的位置
        if(t!=i)
        {
            swap(h[t],h[i]);
            i=t;
        }
        else
            flag=1;
    }
    return ;
}

我们通过改变I的下标,就可以求出一个小根堆。

void creat()
{
    int i;
    for(i=n/2-1;i>=0;i--)
        siftdown(i);
    return;
}

这样我们就建立了一个小根堆。

堆排序

建立了小根堆之后,如何进行排序呢。
很简单我们只需要不断输出根节点的值,并不断向上调整,就可以了,这也是不断删除的操作,在输出之后删除。

int deletemax()
{
    int t;
    t=h[0];//将根节点值保存
    h[0]=h[n-1]; //将大的数赋值到根节点
    n--;
    siftdown(0);//调整为小根堆
    return t;
}

例题

例题链接
这个题嘛,是个水题,有很多解法,比如我们可以贪心一直找两个最小的相加,比如我们可以用二叉堆,再比如我们可以用stl里的优先队列。
但是我们今天是学二叉堆,就写写二叉堆的写法。

#include <iostream>
#include <cstdio>
using namespace std;
int fru[10010],n,i;
void siftdown(int i)//向下调整函数
{
    int flag=0,t;
    while(i*2<n&&flag==0)
    {
        if(fru[i]<fru[2*i])
            t=i;
        else
            t=2*i;
        if(i*2+1<n)
        {
            if(fru[t]>fru[i*2+1])
                t=2*i+1;
        }
        if(t!=i)
        {
            swap(fru[t],fru[i]);
            i=t;
        }
        else
            flag=1;
    }
}
void siftup(int i)
{
    int flag=0;
    if(i==0)
        return;
    while(flag=0&&i!=0)
    {
        if(fru[i]<fru[i/2])
            swap(fru[i],fru[i/2]);
        else
            flag=1;
        i=i/2;
    }
}
void creat()
{
    for(i=n/2-1; i>=0; i--)
        siftdown(i);
}
int  deletemax()
{
    int t;
    t=fru[0];
    fru[0]=fru[n-1];
    siftdown(0);
    n--;
    return t;
}
int main()
{
    while(cin>>n)
    {
        int sum=0;
        for(i=0; i<n; i++)
            cin>>fru[i];
        creat();
        while(n>1)
        {
            int x=deletemax();
            int y=deletemax();
            sum=sum+x+y;
            n++;
            fru[n-1]=x+y;
            siftup(n-1);
        }
        cout<<sum<<endl;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值