📅  最后修改于: 2023-12-03 14:54:13.142000             🧑  作者: Mango
弱堆 (Weak Heap, W-Heap) 是由 Andersson 于 1989 年提出的一种数据结构,它是一种高效的优先队列实现。弱堆可以支持常数时间的插入和合并操作,平均复杂度为 $O(\log n)$ 的删除操作。
故名思义,弱堆比一般的堆数据结构要更为松弛,它减少了精确的堆性质要求,而仅关注了一种较弱的性质。相对于其他堆实现,弱堆在存储与时间复杂度上都有优势,特别适合于大规模的数据处理。
弱堆的基本思路是采用自底向上的方式,将二叉堆 (binary heap) 转化为一颗多路树,以减少树的层数、堆的规模和操作时间。
根据性质,二叉堆中的树高度约为 $\log n$,它的效率很大程度上受到了树高的影响。弱堆在此基础上,允许树高超过 $\log n$,但保证了名义上的 $\log n$ 平均时间复杂度。进一步地,弱堆还容许在删除之后将堆缩容,即将底层的节点下移到上一层。
弱堆的主要性质有:
弱堆主要支持以下几个操作:
Make-Heap
: 创建一个新堆。Insert
: 将一个元素插入到堆中。Delete-Maximum
: 删除堆中最大元素并返回其值。Merge
: 合并两个堆。Decrease-Key
: 降低指定元素的关键字。其中,Make-Heap
和 Merge
操作的复杂度均为 $O(1)$;Insert
和 Decrease-Key
操作的复杂度均为 $O(\log n)$;Delete-Maximum
操作的平均复杂度为 $O(\log n)$,最坏复杂度为 $O(\log^2 n)$。
弱堆可以用多种方式实现,最简单的是使用一个二叉堆来存储弱序列。这种实现方法比较清晰易于理解,但空间复杂度较高。一种更为高效的实现方法是使用一个大小为 $O(\log n)$ 的数组来存储前驱,并在堆的删除和合并操作中实现路径压缩 (path compression)。
以下是 C++ 代码实现:
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1e5 + 10;
int n, m;
int q[N], ne[N];
int h[N], idx;
void insert(int x)
{
q[++idx] = x;
int k = idx;
while (k > 1 && q[ne[k]] < q[k])
swap(ne[k], k);
ne[k] = h[x], h[x] = k;
}
int removeMax()
{
int r = q[1];
q[1] = q[idx--];
int k = 1;
while (k * 2 <= idx)
{
int son = k * 2;
if (son + 1 <= idx && q[son + 1] > q[son])
son++;
if (q[k] < q[son])
{
swap(q[k], q[son]);
k = son;
}
else
break;
}
for (int i = h[r]; i; i = ne[i])
if (q[i] != r)
insert(q[i]);
memset(h, 0, sizeof h);
return r;
}
int main()
{
cin >> n >> m;
while (n--)
{
int x;
cin >> x;
insert(x);
}
while (m--)
cout << removeMax() << endl;
return 0;
}
弱堆是一种简单而高效的数据结构,它在 NP-hard 问题求解中被广泛使用。它充分利用了堆的特性,又避免了传统堆数据结构中限制较多的条件,从而达到了优秀的性能表现。如果您正在开发一个对时间和空间都有严格要求的应用程序,弱堆将是一个非常好的选择。