堆
功能(操作)
插入一个数
求集合中的最小值
删除最小值
额外的操作
删除任意一个元素
修改任意一个元素
堆的本质
二叉树,且是完全二叉树(即除了最后一层的节点,其他层的节点的分支都有两个,最后一层的节点从左到右排列)
小根堆的特点
从上往下看,每个节点的值依次增高
所以根节点是最小的值
再往下
左边的节点比他下面的两个节点还小,右边的节点也比他下面的两个节点还小
堆的存储
用一维数组存储所有的节点
对于根节点 就存储在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);
删除任意一个元素
直接让最后一个节点覆盖掉该节点,然后对他进行up和down操作
因为up和down只会执行一个,那么他是比原来节点大于等于小于都是无所谓的
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);
}
}