释迦牟尼的赛博生命
AI summary
这篇文章介绍了C++中堆的实现。堆是完全二叉树的线性存储形式。文章讨论了普通堆和优先队列的实现,以及堆的函数make_heap、push_heap、pop_heap和sort_heap。make_heap函数将一段现有的数据转化成一个堆,默认情况下生成的是一个大堆。push_heap函数用于往堆中插入一个数据。pop_heap函数是实现调整工作,将堆顶的元素与最后一个元素交换,然后再调整,在将剩余其他元素重新恢复成堆结构。sort_heap函数的功能就相当于多次调用pop_heap,将元素排序。
Published
State
Unread
Tags
blog
GJJ's Blog
在上周的一场周赛中,要用到堆,但是我并不知道如何初始化堆,虽然并没有很影响通过速度,但还是从大佬的代码中学到了很多:

普通堆的实现:

堆其实就是完全二叉树的线性存储形式
notion image
#include<bits/stdc++.h> using namespace std; const int N = 100010; int h[N], ph[N], hp[N], cnt; int n, idx = 0, x, c; string op; 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() { cin >> n; while(n--) { cin >> op; if(op == "I") { scanf("%d", &x); cnt++; idx++; ph[idx] = cnt, hp[cnt] = idx; h[cnt] = x; up(cnt); } else if(op == "D") { scanf("%d", &x); x = ph[x]; heap_swap(x, cnt); cnt --; up(x); down(x); } else if(op == "C") { scanf("%d %d", &x, &c); x = ph[x]; h[x] = c; up(x); down(x); } else if(op == "DM") { heap_swap(1, cnt); cnt--; down(1); } else { printf("%d\n", h[1]); } } return 0; }
以上并不是本篇文章重点,代码下去自己背去,下边才是重点

优先队列:

声明:
priority_queue<int> q; // 大根堆 priority_queue<int, vector<int>, greater<int>> q; // 小根堆 priority_queue<pair<int, int>>q;
操作:
💡
push // 把元素插入堆 pop // 删除堆顶元素 top // 查询堆顶元素(最大值)

堆的函数:

C++的STL提供了make_heap、push_heap、pop_heap、sort_heap等算法,它们用来将一个随机存储的数组或者容器等转换为一个heap。
这里所说的转换为heap意思是将原来的存储顺序改变,将转换成的堆层序遍历后所得到的元素顺序作为数组或者容器新的元素顺序(实质上就是对原来的数据用一个算法换了一下元素顺序)。
1、make_heap
函数声明:
template <class RandomAccessIterator> void make_heap(RandomAccessIterator first, RandomAccessIterator last); template <class RandomAccessIterator, class Compare> void make_heap(RandomAccessIterator first, RandomAccessIterator last, Compare comp);
  • make_heap的功能是将一段现有的数据转化成一个堆,默认情况下生成的是一个大堆
  • 在第一种声明中,参数为迭代器类型,当我们传入一个左闭右开的区间[first, last)时,它会自动把这个区间的数据转化成一个大堆。 在第二种声明中,多了一个模板参数,用于我们给他传入一个比较类型,用这个比较类型来实现小堆的转化。当我们想实现小堆转化时,首先要#include <functional>,然后参数中使用great<int>()
  • 需要注意的是当make_heap中使用了greater()后,后面pop_heap、push_heap和sort_heap都需要加上greater()。 生成大堆的代码:
2、push_heap
函数声明:
template <class RandomAccessIterator> void push_heap(RandomAccessIterator first, RandomAccessIterator last); template <class RandomAccessIterator, class Compare> void push_heap(RandomAccessIterator first, RandomAccessIterator last, Compare comp);
  • push_heap的功能是往堆中插入一个数据。
  • 它的默认前提是区间[first, last - 2]已经满足堆结构,并且要插入的数据已经插入到区间的最后一个位置。
  • 对于基于vector生成的heap:
vetor<int> p; p.push_back(1); p.push_back(2); p.push_back(3); p.push_back(4); makde_heap(p.begin(), p.end()); //建堆 p.push_back(6); push_heap(p.begin(), p.end());
3、pop_heap
函数声明:
template <class RandomAccessIterator> void pop_heap(RandomAccessIterator first, RandomAccessIterator last); template <class RandomAccessIterator, class Compare> void pop_heap(RandomAccessIterator first, RandomAccessIterator last, Compare comp);
  • pop_heap的功能也是实现调整工作,但是是删除前的调整。
  • pop_head会将堆顶的元素与最后一个元素(注意最后一个元素是last的上一个元素)交换,然后再调整,将除了最后一个元素的剩余其他元素重新恢复成堆结构。需要注意的是每次pop一个元素后,下一次pop时就需要将区间缩小1(last减1)。
vetor<int> p; p.push_back(1); p.push_back(2); p.push_back(3); p.push_back(4); makde_heap(p.begin(), p.end()); //建堆 push_pop(p.begin(), p.end()); p.pop_back(6);
4、sort_heap
函数声明:
template <class RandomAccessIterator> void sort_heap(RandomAccessIterator first, RandomAccessIterator last); template <class RandomAccessIterator, class Compare> void sort_heap(RandomAccessIterator first, RandomAccessIterator last, Compare comp);
  • sort_heap的功能就相当于多次调用heap_pop,
  • 我们每次调用heap_pop都将栈顶元素与最后元素进行交换(将栈中最大元素与栈中最后一个元素交换),然后last减1,重复调用heap_pop一直到只剩下1个元素,我们就实现了将元素排序。
Loading...
rail1dd
rail1dd
ENFJ|复古未来|跨界融合|自我探索
公告

评论相关

评论区邮箱填写qq数字邮箱即可抓取qq头像
网址部分为选填

数字学习

倒腾二进制世界的进程笔记,完全未入门级别,看一乐呵
毕竟属于基层和载体,找到合适自己的后也就没有大动过

风格认知

书影音游,整合在这,也不是想写月报,看心情

生存指南

如题,此处是更好的在现实世界生存下去的个人指南,本来想学学约翰·威尔逊的十万个怎么做,但本人似乎没那么跳脱,而且城市也不如纽约来得魔幻,so,这里都是一些超现实的shxt

城市漫游

取名来自文德斯的爱丽丝城市漫游记,所以梦想是真的能末路狂花,仗剑走天涯,但目前平淡的日常生活中也会偶尔出现一些奇妙体验,遂记录
*加密文章密码为同行人生日