一、定义
维护一个数据集合,堆是一个完全二叉树。
那么什么是二叉树呢?
如图:
二、关于小根堆实现

性质:每个根节点都小于等于左右两边,所以树根为最小值。
2.1、堆存储(用一维数组来存)

记住规则:x(根)的左儿子 = x * 2;
x(根)的右儿子 = x * 2 + 1
样例如图:

2.2、操作
2.2.1、操作1、down(x) :节点下移
如果把一个值变大了,就让他往下移动。
样例:
2.2.2、操作2、 up(x):节点向上移
如果把某一个值变小了,就让他向上移动
样例:

2.3、如何手写一个堆?
注意:下标要从1开始,heap[]堆,size:堆长度
如图:

这里后面会有对应的例题,详细解释4、5操作
三、例题:
3.1、堆排序 :此题来源于acwing

图解:

 上述两个图来自于B站董晓算法的视频
AC代码:
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5+10;
int h[N],sz;
int n,m;
void down(int u)
{
    int t = u;
    if(u*2 <= sz && h[u*2] < h[t]) t = u * 2;
    if(u*2+1 <= sz && h[u*2+1] < h[t]) t = u * 2 + 1;
    if(t != u)
    {
        swap(h[t],h[u]);
        down(t);
    }
}
int main()
{
    scanf("%d %d", &n, &m);
    for(int i=1;i<=n;i++) scanf("%d ",&h[i]);
    sz = n;
    //构建堆,从n/2序号开始创建,把最小值挪到树根,相当于忽略最后一层
    for(int i=n/2;i>=1;i--)
    {
        down(i);
    }
    while (m -- )
    {
        printf("%d ",h[1]);
        h[1] = h[sz];
        sz--;
        down(1);
    }
    return 0;
}
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5+10;
int h[N],sz;
int n,m;
//下沉
void down(int u)
{
    int t = u;
    //根据小根堆性质,保证父亲节点是小于左右两个儿子的
    if(u*2 <= sz && h[u*2] < h[t]) t = u * 2;
    if(u*2+1 <= sz && h[u*2+1] < h[t]) t = u * 2 + 1;
    if(t != u)
    {
        swap(h[t],h[u]);//如果不同,需要交换两个节点
        down(t);//继续下沉
    }
}
int main()
{
    scanf("%d %d", &n, &m);
    for(int i=1;i<=n;i++) scanf("%d ",&h[i]);
    sz = n;//记得传一下长度给sz。
    //构建堆,从n/2序号开始创建,把最小值挪到树根,相当于忽略最后一层
    for(int i=n/2;i>=1;i--)
    {
        //这里非常巧妙,从后面开始去下沉,等到树根的时候,会第一个开始down,
        //会排好,不用担心乱序
        down(i);
    }
    while (m -- )
    {
        printf("%d ",h[1]);//取出最小值
        h[1] = h[sz]; //根据规则操作后续步骤
        sz--;
        down(1);
    }
    return 0;
} 
3.2、模拟堆:此题同样来源于acwing


思路:
由于,此题需要记录一下第k个插入的数,所以需要用两个映射的数组去维护一下第k个插入的数,和当前堆中元素的下标,此处附上一位acwing评论区的一位大佬的讲解,我觉得讲的非常好,可以帮助此题理解两个数组的含义,建议先看代码,再看这个。代码中有详细注释,如果有错误欢迎指出~。

AC代码:
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 1e5+10;
//h[]用来存堆,ph[]用来存第k个数对应的堆里的下标
//hp[]用来存堆下标下是第几个存入的数
int h[N],ph[N],hp[N],sz;
int n,m;
void heap_swap(int a,int b)
{
    swap(h[a],h[b]);//交换堆中的两个数值
    swap(hp[a],hp[b]);//在堆中对应的下标下是第几个存入的数,交换一下
    swap(ph[hp[a]],ph[hp[b]]);//交换一下堆中的下标
}
//下沉
void down(int u)
{
    int t = u;
    if(u*2 <= sz && h[u*2] < h[t]) t = u * 2;
    if(u*2+1 <= sz && h[u*2+1] < h[t]) t = u * 2 + 1;
    if(t != u)
    {
        heap_swap(t,u); //由于后面需要找到第k的数,所以不能用普通的交换,需要用我们手写的交换函数
        down(t);//下沉,直到不满足位置
    }
}
//上浮
void up(int u)
{
    //与根部比较,如果父亲大于儿子就需要上浮
    while(u/2 && h[u/2] > h[u]){
        heap_swap(u/2, u);
        u = u/2;
    }
}
int main()
{
    scanf("%d", &n);
    while (n -- )
    {
        char op[10];//根据题目要求输入字符串
        scanf("%s", op);
        if(!strcmp(op,"I"))
        {
            int x;
            scanf("%d", &x);
            m++;//第几个插入的
            sz++;//当前下标
            //ph表示第k插入的数在堆中的下标是多少
            //hp表示该数堆中下标对应第几个插入的数
            ph[m] = sz,hp[sz] = m;
            h[sz] = x;//堆中存入x
            up(sz);//上浮
        }
        else if(!strcmp(op,"PM")) //找到最小的数,就是树根
        {
            printf("%d\n",h[1]);
        }
        else if(!strcmp(op,"DM")) //注意交换后sz--;
        {
            heap_swap(1,sz);
            sz--;
            down(1);
        }
        else if(!strcmp(op,"D")) //删除第k个数
        {
            int k;
            scanf("%d", &k);
            k = ph[k]; //找到第k个数下的堆中的元素下标
            heap_swap(k,sz);
            sz--;
            down(k),up(k);//down和up只会执行一个操作,因为要么大,要么小
        }
        else
        {
            int k,x;
            scanf("%d %d", &k,&x);
            k = ph[k];
            h[k] = x;
            down(k),up(k);//同理
        }
    }
    return 0;
} 
看会以上,大家可以去做一下这个题:
P3378 【模板】堆 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题解在这里:
P3378 【模板】堆-CSDN博客
感谢观看~












![[Halcon学习笔记]标定常用的Halcon标定板规格及说明](https://img-blog.csdnimg.cn/img_convert/fa6de7a12f2f31d0bc74b341454970f3.webp?x-oss-process=image/format,png)






