【数据结构】数组模拟堆

大根堆/小根堆定义

  • 大根堆:任意一个节点的值大于等于它的子节点的值的二叉堆
  • 小根堆:任意一个节点的值小于等于它的子节点的值的二叉堆

数组存储树结构

  • 根节点在数组中的位置是1
  • 第n个位置的子节点分别在2n和 2n+1,因此,第1个位置的子节点在2和3,第2个位置的子节点在4和5,以此类推
  • 节点n的父节点为n/2(向下取整)
    【数据结构】数组模拟堆_第1张图片

up操作

如图,该二叉堆是一个大根堆(数字代表节点值)
【数据结构】数组模拟堆_第2张图片
假设节点值被修改为2,那么2要被调整到上面,也就是所谓的up操作
【数据结构】数组模拟堆_第3张图片


down操作

接上,如果该节点被修改为8,那么它就应该沉下去,也即是对应于down操作
【数据结构】数组模拟堆_第4张图片
代码

  • 对于一个根节点u,找出它本身、左儿子、右儿子三者中最小的数字,如果不是根节点本身,就与根节点进行交换,然后继续down下去
void down(int u)
{
    int t=u,left=2*u,right=2*u+1;
    if(left<=sizen&&h[left]<h[t]) t=left;
    if(right<=sizen&&h[right]<h[t]) t=right;
    if(u!=t)
    {
        swap(h[t],h[u]);
        down(t);
    }
}   

堆的各类操作汇总

图片来源:acwing
在这里插入图片描述

  • 插入操作:相当于在数组末尾加上数字(对应++size),然后执行up操作
  • 求最小值:直接访问h[1]即可
  • 删除最小值:h[1]替换为h[size],然后把size–,相当于剔除h[1]
  • 删除任意元素:将h[k]替换为h[size],size–,之后执行up和down操作,相当于剔除h[k]

简单应用题

输入一个长度为 n 的整数数列,从小到大输出前 m 小的数。

#include 
#include 

using namespace std;

const int N= 1e5+10;

int n,m,sizen;
int h[N];

void down(int u)
{
    int t=u,left=2*u,right=2*u+1;
    if(left<=sizen&&h[left]<h[t]) t=left;
    if(right<=sizen&&h[right]<h[t]) t=right;
    if(u!=t)
    {
        swap(h[t],h[u]);
        down(t);
    }
}   

int main()
{
    cin>>n>>m;
    sizen=n;
    for(int i=1;i<=n;i++ ) cin>>h[i];
    //注意到要从n/2开始down操作,直到down到1,n/2表示二叉堆的最后一个节点的父节点
    for(int i=n/2;i>=1;i--) down(i);
    for(int i=1;i<=m;i++)
    {
        cout<<h[1]<<' ';
        h[1]=h[sizen];
        sizen--;
        down(1);
    }
    return 0;
}

你可能感兴趣的:(算法基础)