📜  弱堆(1)

📅  最后修改于: 2023-12-03 14:54:13.142000             🧑  作者: Mango

弱堆 - A Simple and Efficient Data Structure

Introduction

弱堆 (Weak Heap, W-Heap) 是由 Andersson 于 1989 年提出的一种数据结构,它是一种高效的优先队列实现。弱堆可以支持常数时间的插入和合并操作,平均复杂度为 $O(\log n)$ 的删除操作。

故名思义,弱堆比一般的堆数据结构要更为松弛,它减少了精确的堆性质要求,而仅关注了一种较弱的性质。相对于其他堆实现,弱堆在存储与时间复杂度上都有优势,特别适合于大规模的数据处理。

Basic Idea

弱堆的基本思路是采用自底向上的方式,将二叉堆 (binary heap) 转化为一颗多路树,以减少树的层数、堆的规模和操作时间。

根据性质,二叉堆中的树高度约为 $\log n$,它的效率很大程度上受到了树高的影响。弱堆在此基础上,允许树高超过 $\log n$,但保证了名义上的 $\log n$ 平均时间复杂度。进一步地,弱堆还容许在删除之后将堆缩容,即将底层的节点下移到上一层。

Properties

弱堆的主要性质有:

  1. 弱堆的深度为 $O(\log n)$。
  2. 弱堆中除最大值节点外,其余节点都有一个前驱 (predecessor)。
  3. 对于任意的节点 $x$,其前驱 $y$ 都满足 $y.key \geq x.key \cdot \alpha$,其中 $\alpha$ 是一个小于 1 的常数。
  4. 所有的前驱都构成了一条从根到最大值节点的路径,称为弱序列 (weak sequence)。
  5. 弱序列每个节点的左子女为左子女的前驱,右子女为右子女的前驱,它们与对应的前序遍历的节点一一对应。
  6. 弱堆的含义可以表述为:对于所有的内部节点 $x$,其关键字不大于其子女和前驱节点的关键字的平均值。
Operations

弱堆主要支持以下几个操作:

  • Make-Heap: 创建一个新堆。
  • Insert: 将一个元素插入到堆中。
  • Delete-Maximum: 删除堆中最大元素并返回其值。
  • Merge: 合并两个堆。
  • Decrease-Key: 降低指定元素的关键字。

其中,Make-HeapMerge 操作的复杂度均为 $O(1)$;InsertDecrease-Key 操作的复杂度均为 $O(\log n)$;Delete-Maximum 操作的平均复杂度为 $O(\log n)$,最坏复杂度为 $O(\log^2 n)$。

Implementation

弱堆可以用多种方式实现,最简单的是使用一个二叉堆来存储弱序列。这种实现方法比较清晰易于理解,但空间复杂度较高。一种更为高效的实现方法是使用一个大小为 $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;
}
Conclusion

弱堆是一种简单而高效的数据结构,它在 NP-hard 问题求解中被广泛使用。它充分利用了堆的特性,又避免了传统堆数据结构中限制较多的条件,从而达到了优秀的性能表现。如果您正在开发一个对时间和空间都有严格要求的应用程序,弱堆将是一个非常好的选择。