堆分为小根堆和大根堆,小根堆的父节点都要比子节点的值小,大根堆相反。 堆的存储使用一个一维数组来存储的,数组的下标我们是从1开始的,根节点下标为x的左孩子的下标为2x,右孩子的下标为2x+1. 如何手写一个堆? 1.插入一个数 2.求集合当中的最小值 3.删除最小值 4.删除任意一个元素 5.修改任意一个元素

例一:堆排序

#include #include using namespace std;const int N = 100010;int n, m;int h[N], cnt;//数组h表示堆,cnt表示堆里面存储的元素个数void down(int u){int t = u;//t存储三个结点中存在的最小的结点的下标,初始化为当前结点uif(u * 2 <= cnt && h[u * 2] < h[t]) t = u * 2;// 左子节点存在并且小于当前结点,更新t的下标if(u * 2 + 1 <= cnt && h[u * 2 + 1] < h[t]) t = u * 2 + 1;//右子节点存在并且小于当前结点,更新t的下标if(u != t)//如果t==u意味着不用变动,u就是三个结点中拥有最小值的结点下标,否则交换数值{swap(h[u], h[t]);//交换数值后,t这个结点存储原本u的值,u存储存储t的值(三个数中的最小值)//u不用调整了,但t情况不明,可能需要调整。直到它比左右子节点都小down(t);}}int main(){scanf("%d%d", &n, &m);for(int i = 1; i <= n; ++i) scanf("%d", &h[i]);cnt = n;for(int i = n / 2; i; --i) down(i);//把堆初始化成小根堆,从二叉树的倒数第二行开始,把数字大的下沉while(m--){printf("%d ", h[1]);h[1] = h[cnt--];down(1);}}

例题二:模拟堆

#include #include using namespace std;const int N = 1e5 + 10;//数组h表示堆,数组ph表示存放第k个插入点的下标//数组hp表示存放堆中点的插入次序,cnt表示记录的是堆当前的个数多少//ph数组主要用于帮助从idx映射到下标kint h[N], ph[N], hp[N], cnt;//hp[u]=k 则ph[k]=u 是映射关系//之所以要进行这样的操作是因为 经过一系列操作 堆中的元素并不会保持原有的插入顺序//从而我们需要对应到原先第K个堆中元素//如果理解这个原理 那么就能明白其实三步交换的顺序是可以互换 //h,hp,ph之间两两存在映射关系 所以交换顺序的不同对结果并不会产生影响void heap_swap(int a, int b){swap(ph[hp[a]], ph[hp[b]]);swap(hp[a], hp[b]);swap(h[a], h[b]);}void down(int u){int t = u;if(u * 2 <= cnt && h[u * 2] < h[t]) t = u * 2;if(u * 2 + 1 <= cnt && h[u * 2 + 1] < h[t]) t = u * 2 + 1;if(u != t){heap_swap(u, t);down(t);}}void up(int u){while(u / 2 && h[u] < h[u / 2]){heap_swap(u, u / 2);u >>= 1;}}int main(){int n, m = 0;//m用来记录插入的数的个数//注意m的意义与cnt是不同的 cnt是记录堆中当前数据的多少//对应上文 m即是hp中应该存的值scanf("%d", &n);while(n--){char op[5];int k, x;scanf("%s", op);if(op == "I"){scanf("%d", &x);cnt++;m++;ph[m] = cnt, hp[cnt] = m;up(cnt);}else if(op == "PM") printf("%d", h[1]);else if(op == "DM"){heap_swap(1, cnt);cnt--;down(1);}else if(op == "D"){scanf("%d", &k);int u = ph[k];//这里一定要用u=ph[k]保存第k个插入点的下标heap_swap(u, cnt);//因为在此处heap_swap操作后ph[k]的值已经发生 cnt--;up(u);down(u);}else{scanf("%d%d", &k, &x);k = ph[k];h[k] = x;up(k);down(k);}}return 0;}