1、定义
堆是一种叫做完全二叉树的数据结构,
完全二叉树 :除了最后一层之外,上面全部都是满的,最后一层也是从左往右排列的
2、性质
在小堆根中:每一个点都是小于等于左右两边的子节点的值
堆的存储:
一般情况都是将其存储在数组中的,而且一般以索引1为起点
因为数组的排序有个公式 :
x的左节点 : 2x
x的右节点 : 2x+1
可以得到下面这个图
核心操作
堆的所有的操作都可以使用这两个操作来完成
down(x)
如果把一个值变大了,就要把他往下移
比如说在这个图中,将头节点的值改变为6,数值变大了,应该往下移动
从两个子节点和当前节点中 找到一个最小值,为3,然后进行交换,如下图
然后再跟三个节点中的最小值进行交换
up(x)
跟down相反,如果把一个值变小了,就要把他往上移
跟父节点进行比较,如果比父节点小,就进行交换
然后再将2和3进行比较
代码分析
定义
heap表示正整个堆 size表示当前堆的大小
1、插入一个数x
//在堆的最底层插入一个数字x
heap[ ++ size] = x;
//然后将x不断上移
up(size);
2、求集合当中的最小值
heap[1];
3、删除最小值
//先用最后一个点覆盖掉第一个点
heap[1] = heap[size];
//然后把最后一个点删除
size --;
//最后让1号点往下走
down(1);
4、删除任意一个元素k
跟删除最小值类似
heap[k] = heap[size];
size --;
//这里会出现三种情况,
//1、要么数字比较大 需要进行down操作
//2、要么数字比较小 需要进行up操作
//3、要么数字刚好在这个位置 不需要进行操作
//三种情况只会出现一次,所以直接先进行down,在进行up操作
down(k);up(k);//在这里虽然写了两个操作,但是实际只会执行一个操作
5、修改任意一个元素
heap[k] = x;
//跟删除任意一个元素同理
down(k);up(k);
6、代码中的down操作
如果想要进行daown操作 就要去看这个三角形上面的那个点是不是三个点中的最小值
int h[N];
int se;
void down(int u)
{
//用t来表示三个点中的最小值
int t = u;
//判断是否有左儿子 u * 2 <= size
//并且判断左儿子是否小于 h[t]
//都满足 则将t 更换为左儿子
if (u * 2 <= se && h[u * 2] <= h[t]) t = u * 2;
//同理 判断右儿子
if (u * 2 + 1 <= se && h[u * 2 + 1] <= h[t]) t = u * 2 + 1;
//最终t表示的就是三个点中的最小点
//如果t点不是父节点 交换节点
if (u != t)
{
swap(h[u],h[t]);
down(t);
}
}
7、代码中的down操作
void up(int u)
{
while (u / 2 != 0 && h[u / 2] >= h[u])
{
swap(h[u/2],h[u]);
u /= 2;
}
}
8、如何去将数组变换成一个堆
for (int i = 1; i <= n; i++) cin >> h[i];
se = n;
//去将数组变换成一个堆
for (int i = n / 2; i; i--) down(i);
题目:堆排序
输入一个长度为 𝑛 的整数数列,从小到大输出前 𝑚 小的数。
输入格式
第一行包含整数 𝑛 和 𝑚。
第二行包含 𝑛 个整数,表示整数数列。
输出格式
共一行,包含 𝑚 个整数,表示整数数列中前 𝑚 小的数。
数据范围
1<= m <= n <= 10^5
1<= 数列中的元素 <= 10^9
输入样例:
5 3
4 5 1 3 2
输出样例:
1 2 3
代码
#include <iostream>
using namespace std;
const int N = 100010;
int h[N];
int se;//记录堆里面一共有几个值
void down(int u)
{
//用t来表示三个点中的最小值
int t = u;
//判断是否有左儿子 u * 2 <= size
//并且判断左儿子是否小于 h[t]
//都满足 则将t 更换为左儿子
if (u * 2 <= se && h[u * 2] <= h[t]) t = u * 2;
//同理 判断右儿子
if (u * 2 + 1 <= se && h[u * 2 + 1] <= h[t]) t = u * 2 + 1;
//最终t表示的就是三个点中的最小点
//如果t点不是父节点 交换节点
if (u != t)
{
swap(h[u],h[t]);
down(t);
}
}
void up(int u)
{
while (u / 2 != 0 && h[u / 2] >= h[u])
{
swap(h[u/2],h[u]);
u /= 2;
}
}
int main()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> h[i];
se = n;
//去将数组变换成一个堆
for (int i = n / 2; i; i--) down(i);
while (m--)
{
cout << h[1] << " ";
h[1] = h[se];
se--;
down(1);
}
return 0;
}