Algorithm|【algorithm】算法学习----堆

堆 堆涉及到的五个问题:

  • 插入一个数
  • 求集合中的最小值
  • 删除最小值
  • 插入任意一个元素
  • 删除任意一个元素
对于堆,这里需要使用树来存储。
这里以小根堆来举例:
什么是小根堆,小根堆就是父节点比其两个孩子节点都要小。
由此我们可以想到根节点就是当前堆的最小值。
堆其实是一个完全二叉树。
什么是完全二叉树?
就是除了最后一层,其余层都是满度(都是2),并且最后一层的是从左到右排列
Algorithm|【algorithm】算法学习----堆
文章图片

但是如果使用节点point+value比较麻烦并且不适用于算法题里面。
所以我们使用一维数组来存储:
Algorithm|【algorithm】算法学习----堆
文章图片

因此我们对于这个完全二叉树每次插入一个节点就有两个操作down()up()操作。
要么网上调,要么往下挑。
down的逻辑:<还是以小根堆为例>
如果当前的节点比其两个孩子的节点都要大,因此将该节点与其孩子节点中最小的进行交换。
Algorithm|【algorithm】算法学习----堆
文章图片

up的逻辑与:
如果当前的节点比其父亲节点要大,则交换这两个节点,一直到上升到合适的位置
Algorithm|【algorithm】算法学习----堆
文章图片

因此对于开头的五个问题可以有以下解法:
假设堆为heap[N],heap数组的大小为size.
插入一个数 heap[++size]=x; up(x)
求集合中的最小值 heap[1]
删除最小值 使用最后一个元素覆盖根节点,然后删除最后一个元素。然后再让最后一个元素Down() heap[1]=heap[size]; size–; down(1)
插入任意一个元素 heap[k]=x; Down(k)/Up(k);
删除任意一个元素 heap[k]=heap[size]; size–; Down(k)/Up(k);
概念理解题:838. 堆排序 - AcWing题库
正式做题可能会遇到一个问题,我输入的是一个数组,我怎么把它构建成堆呢?
Algorithm|【algorithm】算法学习----堆
文章图片

Algorithm|【algorithm】算法学习----堆
文章图片

#include #include using namespace std; const int N = 1e5+10; int h[N]; int s,m,n; void down(int u) { int t=u; if(2*u<=s&&h[2*u]>n>>m; for(int i=1; i<=n; i++) cin>>h[i]; s=n; //这里就涉及到了如何构建堆: for(int i=n/2; i>=1; i--) down(i); while(m--) { cout<

//Down()函数 void down(int u) { int t=u; if(2*u<=s&&h[2*u]h[u]) swap(h[u/2],h[u]); u/=2; }

练习题:839. 模拟堆 - AcWing题库
其实我们仔细想啊,堆节点的下标我们是知道的,我们只需要一个数组存储下标为i的数是第j次插入的。但是看一下这道题目的要求:
  1. I x,插入一个数 x;
  2. PM,输出当前集合中的最小值;
  3. DM,删除当前集合中的最小值(数据保证此时的最小值唯一);
  4. D k,删除第 k 个插入的数;
  5. C k x,修改第 k 个插入的数,将其变为 x;
知道第k个插入的数我们还得找到对应数的下标,因此我们有了ph与hp这两个互指指针。
ph[k]表示第k个插入的数在数组的下标什么。
hp[k]表示堆里下标为k的点是第几个被插入的
Algorithm|【algorithm】算法学习----堆
文章图片

#include #include #include using namespace std; const int N = 100010; int h[N], ph[N], hp[N], cnt; 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; scanf("%d", &n); while (n -- ) { char op[5]; 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")) { heap_swap(1, cnt); cnt -- ; down(1); } else if (!strcmp(op, "D")) { scanf("%d", &k); k = ph[k]; heap_swap(k, cnt); cnt -- ; up(k); down(k); } else { scanf("%d%d", &k, &x); k = ph[k]; h[k] = x; up(k); down(k); } }return 0; }

补充 strcmp()函数
【Algorithm|【algorithm】算法学习----堆】该函数返回值如下:
  • 如果返回值小于 0,则表示 str1 小于 str2。
  • 如果返回值大于 0,则表示 str1 大于 str2。s
  • 如果返回值等于 0,则表示 str1 等于 str2。

    推荐阅读