堆
功能(操作)
额外的操作
堆的本质
二叉树,且是完全二叉树(即除了最后一层的节点,其他层的节点的分支都有两个,最后一层的节点从左到右排列)
小根堆的特点
从上往下看,每个节点的值依次增高 所以根节点是最小的值 再往下 左边的节点比他下面的两个节点还小,右边的节点也比他下面的两个节点还小
|
堆的存储
用一维数组存储所有的节点
对于根节点 就存储在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);
|
删除最小值 让最后一个节点覆盖掉根节点,然后对根节点down操作
|
heap[1] = heap[size]; size--; down(1);
|
删除任意一个元素 直接让最后一个节点覆盖掉该节点,然后对他进行up和down操作 因为up和down只会执行一个,那么他是比原来节点大于等于小于都是无所谓的
|
heap[k] = heap[size]; size--; up(k),down(k);
|
修改任意一个元素 与删除同理,只不过heap[size]变成了x
|
模板
堆排序
#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);
while (m--) { printf("%d ", h[1]); h[1] = h[se]; se--; down(1); } }
|