voiddown(int i){ // t 代表当前节点和两个子节点中最小的那个节点索引 int t = i; if (i*2 <= sz && h[i*2] < h[t]) t = i*2; if (i*2+1 <= sz && h[i*2+1] < h[t]) t=i*2+1; if (t != i) { swap(h[t], h[i]); down(t); } }
intmain(){ int n, m; scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) scanf("%d", h+i); sz = n; for (int i = sz / 2; i; --i) down(i); while (m--) { printf("%d ", h[1]); h[1] = h[sz--]; down(1); } return0; }
插入
插入元素时将元素添加到末尾,然后使用上滤操作将它交换到合适的位置。
1 2 3 4 5 6 7 8
voidup(int i){ while (i / 2 >= 1 && h[i/2] > h[i]) swap(h[i/2], h[i]), i /= 2; }
voidinsert(int x){ h[++sz] = x; up(sz); }
修改与删除第k插入节点
要像静态链表那样记录下插入的位置,需要用到两个数组,ph 是从第 k 到堆中下标的映射,而 hp 是堆中下标到第 k 的映射,当我们交换两个元素时,如下图所示:
由于我们输入给 swap 的参数只知道堆中的下标,所以才需要 hp 这个数组储存下标对应的 k,然后交换 ph 中的这两个 k 对应的元素下标,随后再把 hp 对应的 k 交换。
这样的话,可以通过 ph[k] 正确地获取到第 k 插入元素的下标。至于这两个数组的名字,可以这样判断:ph 是从外部指向堆,而hp 是从堆指向外部。
相对应地,所有交换的操作都要用 heapswap 交换元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
voidheapswap(int a, int b){ swap(ph[hp[a]], ph[hp[b]]); swap(hp[a], hp[b]); swap(h[a], h[b]); }
voiddown(int k){ int t = k; if (k*2 <= sz && h[k*2] < h[t]) t=k*2; // 这里 h[t] 容易写成 h[k] 因为此时 t 可能等于 2k 所以必须写 h[t] if (k*2+1 <= sz && h[k*2+1] < h[t]) t=k*2+1; if (t != k) heapswap(t, k), down(t); }