Pairing_heap(配对堆)

这个东西在pb_ds中用过。但不知道是什么。然后刚好这几天有空就看了一下。发现其实
很简单。

他事实上就只是维护了一颗树而已。

我们假设现在有 N 个节点,每个节点都带有权值。我们要维护这些节点的一个堆。

假设我们要维护一个小根堆。

对于每个节点i,我们都有一个表 Son 维护他所有的儿子,并且还有一个值 Fai 表示他的父亲。

对于这颗树而言,我们保证每个节点的儿子的权值都要大于等于这个节点的权值

那么最后这颗树的根,设为 Root ,就是权值最小的点。

接下来考虑堆的几个基本操作。

1. Merge(u,v)

表示我们要把u,v这两个堆合并起来,接着返回这个新堆的根。
那么很显然的,我们只需要比较一下 Rootu 的权值和 Rootv 的权值,设权值大的是 y ,另外一个是x,接着直接把 Fay 设为 x ,把y加入到 Sonx 中即可。
最后返回 x 即可。

2. Insert(u,v)

表示我们要往堆u中加入一个权值为 v 的节点。那么我们可以先新建一个堆,里面只有一个权值为v的点。然后把他们Merge在一起就好了。

3. ChangeVal(u, Δ )

表示我们要把节点 u 的权值减去Δ,Δ0.
这是一个在堆中非常常用的操作。
首先,若 u 就是他所处堆的根的话,这个堆没有任何变化。我们直接把权值修改了即可。

否则,我们先把u从这个堆中分离出来。

值得注意的是,我们不修改 Fau Son 集合。我们只把 Fau 设为空。表示他现在是他所处堆的根。至于对 Fau 的影响,我们接下来会考虑。

u 分离出来后。我们直接修改他的权值。然后Merge(u)即可。

4. Pop(u)

表示我们要把 u 这个堆的根给删掉。

这是整个Paring_heap中最复杂(其实也很简单),也是最神奇的地方。

我们知道按照我们上面的操作,我们的根的儿子个数最坏情况下是 O(N) 的。

设根为 Root

我们首先肯定要提取出 Root 的所有儿子。但注意的是,此时 Root Son 表中可能有些并不是他的儿子。但事实上我们只需要判一下 Fav 是否等于 Root 即可。这是不影响时间复杂度的。而且这也简化了ChangeVal的编程复杂度。

假设现在的真正的儿子表就是 Son .

一种简单粗暴的方法就是枚举所有的儿子,然后把他们直接Merge在一起。

由于这个实在是太粗暴了。所以这个的时间复杂度是最差的。。

另外一种方法是,
回忆我们用最普通的堆来 O(N) 建出 N 个元素的堆的做法。

我们维护一个FIFO(first in first out)队列。
一开始先把所有的元素塞进这个队列里。

若当前队列中只有一个元素,那么我们的根就是这个元素。

否则我们取出队首的两个元素,设为x,y,接着再把 Merge(x,y) 塞入队列当中。

这种做法的单次时间复杂度是 O() 的。

但是我们发现这样子建出来的根的儿子个数肯定变少了。

很可惜我并不会分析这个时间复杂度。

时间复杂度

以下复杂度都是期望复杂度

  1. 一种说法是,除了Pop() O(logN) ,其他都是 O(1)
  2. 我看来最早提出这个算法的paper,他的复杂度是除了 Pop ChangeVal O(logN) ,其他都是 O(1)

实用性

我用pairing_heap打了个dijkstra,和priority_queue比较了一下。发现虽然算法一的理论复杂度是 O(NlogN+M) ,但是 Pop 的常数较大。同开了 O2 的priority_queue感觉差不多,还慢了一点,可能要松弛次数比 N <script type="math/tex" id="MathJax-Element-53">N</script>大得多才能体现出优势吧。

代码


struct STACK
{
    int A[MAXN],tot,cnt;

    int Top()
    {
        if (!tot) return ++ cnt;
        return A[tot --];
    }

    void Push(int x)
    {
        A[++ tot] = x;
    }

};

struct Pairing_heap
{
    STACK Edge,Point;
    int Que[MAXN << 1];
    LL Val[MAXN];
    int Final[MAXN],Next[MAXN],Fa[MAXN],Refer[MAXN],Apear[MAXN],To[MAXN],Root;
    int tmp;

    int Merge(int a,int b)
    {
        if (Val[a] > Val[b]) {tmp = a,a = b,b = tmp;}
        int v = Edge.Top();
        To[v] = b,Next[v] = Final[a],Final[a] = v,Fa[b] = a;
        return a;
    }

    void Insert(int x,int y)
    {
        int u = Point.Top();
        Val[u] = y;
        Refer[x] = u,Apear[u] = x;
        if (Root) Root = Merge(u,Root); else Root = u;
    }

    void Extract(int x)
    {
        if (x == Root || !x) return;
        Fa[x] = 0;
    }

    void ChangeVal(int x,int y)
    {
        x = Refer[x];
        Extract(x);
        Val[x] = y;
        if (x != Root) Root = Merge(x,Root);
    }

    void Value(int x,int y)
    {
        if (!Refer[x]) Insert(x,y); else ChangeVal(x,y);
    }

    int Top()
    {
        return Apear[Root];
    }

    void Pop()
    {
        int fi = 0,en = 0;
        for(int i = Final[Root];i;i = Next[i])
        {
            Edge.Push(i);
            if (Fa[To[i]] == Root) Fa[To[i]] = 0,Que[++ en] = To[i];
        }
        Point.Push(Root);
        Refer[Apear[Root]] = Final[Root] = Fa[Root] = 0;
        Apear[Root] = 0;
        Root = 0;
        while (fi < en)
        {
            ++ fi;
            if (fi == en) {Root = Que[fi];return;}
            int u = Que[fi],v = Que[++ fi];
            Que[++ en] = Merge(u,v);
        }
    }
};
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页