如何手写一个堆?
- 插入一个数
- 求集合当中的最小值
- 删除最小值
- 删除任意一个元素
- 修改任意一个元素
堆的基本结构。
性质:
堆是一颗完全二叉树。按照序号来的
除了最后一层,其他都是满的
每一个点都是小于等于儿子
存储
用一维数组来存
x的左儿子是2x,x的右儿子是2x+1
从1开始,0的左儿子是两倍的0
一维数组可以存一个树
在STL里面堆就是优先队列
基本操作
down(x) :
往下调整
某一个点的值改变就需要往下移
up(x):
只需要和父节点比较
插入:
是插入到最后一个 位置。然后不断往上移
删除最小值:
删除头节点很麻烦,删除尾结点很简单
插入删除都是logn
时间复杂度:O(N)算法来初始化。因为最后一排不用动
从下往上down可以保证准确性
递归可以很容易改成循环。
但是这个是logn可以不用改成循环
logn是操作的上界
#include <cstdio>
#include <iostream>
using namespace std;
const int maxn = 1e5 + 10;
int h[maxn], cnt; // size现在的堆中有几个数字
void down(int u)
{
int t = u; // 表示我现在要移动去的地方
if(2 * u <= cnt && h[t] >= h[2 * u]) t = 2 * u;
if(2 * u + 1 <= cnt && h[t] >= h[2 * u + 1]) t = 2 * u + 1;
if(t != u)
{
swap(h[t], h[u]);
down(t);
}
}
void up(int u)
{
while (u / 2) // 1 的时候就进不去了
{
if(h[u] < h[u / 2]) swap(h[u], h[u / 2]);
u /= 2;
}
}
int main()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i ++ ) scanf("%d", &h[i]), cnt ++ ;
for (int i = n / 2; i; i -- ) down(i);
while (m -- )
{
printf("%d ", h[1]);
h[1] = h[cnt];
cnt --;
down(1);
}
}
模拟堆
存映射关系,然后再维护映射 关系
主要是第四个和第五个操作。
pointer heap
strcmp(str1, str2)
左边小就返回负数
左边大就返回正数
相等就返回0
而且char类型的数组是不能用 == 直接比较大小的。
#include <cstdio>
#include <iostream>
#include <string.h>
using namespace std;
const int maxn = 1e5 + 10;
int h[maxn], cnt; // cnt现在的堆中有几个数字
int hp[maxn], ph[maxn]; // ph[k]指的是第k个数所在的下标,hp[k]指的是下标为k的数,存的是第几个数
void heap_swap(int u, int v)
{
swap(h[u], h[v]); // 最后更新才对
swap(ph[hp[u]], ph[hp[v]]); // 下面两个怎么调换都没问题,因为他只是交换,不论是A交换B还是B交换A。效果是一样的。
swap(hp[u], hp[v]); // 先交换坐标比较好,因为后面都是根据坐标来改进的
}
void down(int u)
{
int t = u; // 表示我现在要移动去的地方
if(2 * u <= cnt && h[t] >= h[2 * u]) t = 2 * u;
if(2 * u + 1 <= cnt && h[t] >= h[2 * u + 1]) t = 2 * u + 1;
if(t != u)
{
heap_swap(t, u);
down(t);
}
}
void up(int u)
{
while (u / 2) // 1 的时候就进不去了
{
if(h[u] < h[u / 2]) heap_swap(u, u / 2);
u /= 2;
}
}
int main()
{
int n, m = 0;
cin >> n;
char op[10];
while (n -- )
{
int k, x;
scanf("%s", op);
if (!strcmp(op, "I"))
{
scanf("%d", &x);
cnt ++ ;
m ++ ;
ph[m] = cnt;
hp[cnt] = m, h[cnt] = x;
up(cnt);
}
else if (!strcmp(op, "PM")) printf("%d\n", h[1]);
else if (!strcmp(op, "DM"))
{
//h[1] = h[cnt];
heap_swap(1, cnt); // 是heap_swap交换过去,不能直接swap
cnt --;
down(1);
}
else if (!strcmp(op, "D"))
{
scanf("%d", &k);
k = ph[k];
heap_swap(k, cnt);
cnt --;
down(k), up(k);
}
else
{
scanf("%d%d", &k, &x);
h[ph[k]] = x;
down(ph[k]), up(ph[k]);
}
}
return 0;
}