功能(操作)

插入一个数
求集合中的最小值
删除最小值

额外的操作

删除任意一个元素
修改任意一个元素

堆的本质

二叉树,且是完全二叉树(即除了最后一层的节点,其他层的节点的分支都有两个,最后一层的节点从左到右排列)

在这里插入图片描述

小根堆的特点

从上往下看,每个节点的值依次增高
所以根节点是最小的值
再往下
左边的节点比他下面的两个节点还小,右边的节点也比他下面的两个节点还小

堆的存储

用一维数组存储所有的节点

对于根节点 就存储在q[1]

根节点的左节点 存储在q[2],右节点存储在q[3]

左节点的左节点存储在q[4], 右节点存储在q[5]

右节点的左节点存储在q[6], 右节点存储在q[7]

因此可以得出一个规律,从根节点开始,节点设为x

x下面的左节点就存储在q[2 * x],右节点就存储在q[2 * x + 1]

在这里插入图片描述

down操作

在这里插入图片描述

简单来说就是把一个节点往下移
如果一个节点的值改变了,那么他在堆里应该放在适合的位置

改变时的树

down操作的原理是
如果该节点比他下面的节点大的话,那么就交换一下
如果交换后还比下面的大,那么就再和下面的节点交换
直到符合小根堆的性质

down操作后的树
在这里插入图片描述

up操作

与down操作如出一辙

最后我们可以知道一开始那些操作是如何实现的

用heap存储每一个节点,size作为元素量也是下标

插入一个数 
把要插入的数放到数组最后一个位置,然后对新节点up操作
heap[++size] = x;
up(size);
求集合中的最小值
就是heap[1]
删除最小值
让最后一个节点覆盖掉根节点,然后对根节点down操作
heap[1] = heap[size];
size--;
down(1);
删除任意一个元素
直接让最后一个节点覆盖掉该节点,然后对他进行updown操作
因为updown只会执行一个,那么他是比原来节点大于等于小于都是无所谓的
heap[k] = heap[size];
size--;
up(k),down(k);
修改任意一个元素
与删除同理,只不过heap[size]变成了x
heap[k] = x;
up(k),down(k);

模板

堆排序

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1e5;

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

void down(int u)
{
int t = u;
if (u * 2 <= se && h[u * 2] < h[t])//左节点比较
t = u * 2;
if (u * 2 + 1 <= se && h[u * 2 + 1] < h[t]) //右节点比较
t = u * 2 + 1;

if (u != t)
{
swap(h[u], h[t]);
down(t);
}
}

void up(int u)
{
while (u / 2 && h[u / 2] > h[u])
{
swap(h[u / 2], h[u]);
u /= 2;
}
}
int main()
{
cin >> n >> m;

for (int i = 1;i <= n;++i) scanf("%d", &h[i]);
se = n;

for (int i = n / 2;i;i--) down(i);//时间复杂度接近O(1)

while (m--)
{
printf("%d ", h[1]);
//删除堆顶元素
h[1] = h[se];
se--;
down(1);
}
}