堆
-
如何手写一个堆 堆用于维护数组集合
-
操作:
操作 | 代码 |
---|---|
插入一个数 | heap[++size] = x (在整个堆的最后一个位置插上x) up(size) (不断往上移) |
求集合中的最小值 | heap[1] |
删除最小值 | heap[1] = heap[size]; (用堆的最后一个元素来覆盖堆顶元素) size–;(把移动过来的最后一个点删除) down(1);(让移动过来的1号点往下走直到找到合适的位置) |
删除任意一个元素 | heap[k] = heap[size]; size–; down(k); up(k); (k的位置可能是需要down() 也可能是需要up() 直接两个都写,一定会执行其中之一,且只会执行其中之一) |
修改任意一个元素 | heap[k] = x; down(k); up(k); |
-
结构:完全二叉树(除最后一层节点,上面是满的,最后一层节点从左到右分布)
-
堆的性质:
- 小根堆:每一个点都小于等于左右子节点(递归)— 根节点是所有数据中的最小值
-
堆的存储:
- 一维数组存储
- 1号点是根节点,下标从1开始
- 节点 x 的左儿子是 2x,右儿子是 2x + 1
-
堆的基本操作:
- down(x) 往下调整 时间复杂度O(logn)
- 基本逻辑:从根节点开始,在其左右子节点中找到最小的节点,将最小的节点与根节点交换,一直移动到没有更小的值在其下面
- up(x) 往上调整 时间复杂度O(logn)
- 基本逻辑:从叶子节点开始,只需要与父节点比较(不需要与兄弟节点比较),如果比父节点小,则交换
- down(x) 往下调整 时间复杂度O(logn)
-
建立堆(时间复杂度为O(n))
直接从 n/2 down() 到 n (n/2开始就是不看最后一排)
堆排序
代码:
#include<stdio.h>
#define N 100005
int n, m;
int h[N], size;//size记录总个数
void down(int u) {
int min = u;
int t;
if(u*2 <= size && h[u*2] < h[min]) {
min = u*2;
}
if(u*2 + 1 <= size && h[u*2 + 1] < h[min]) {
min = u*2 + 1;
}
if(min != u) {
t = h[min];//是数去交换,不是指数
h[min] = h[u];
h[u] = t;
down(min);
}
return;
}
int main() {
scanf("%d%d", &n, &m);
int i;
for(i = 1; i <= n; i++) {
scanf("%d", &h[i]);
}
size = n;
for(i = n / 2; i >= 1; i--) {
down(i);//将数组排序
}
while(m--) {
printf("%d ", h[1]);
h[1] = h[size--];
down(1);//每次把第一个数和最后一个交换,然后将第一个数往下排序放到正确位置
}
return 0;
}