可合并优先队列:左偏树和斜堆

有时候需要合并优先队列,对于二叉堆,只能以O(N)的复杂度建堆,因为它除了键值和堆序性质其它什么都没有。

要想把合并的复杂度降为O(LogN),可以用斜堆(左偏树),二项堆等数据结构。斐波拉契堆更牛,很多操作都是O(1)的理论复杂度,但是思维复杂度和编程复杂度……

二项堆了解了一点,但只是半懂不懂地看了点性质之类的,具体的操作还不会。有时间还是想试一下。斐波拉契堆就算了吧……

左偏树的性质是:左子树的零路径长不小于右子树,这就导致树严重向左偏。合并两棵树,比较堆顶元素大小,把一棵树的右子树和另一棵树递归合并即可。如果合并后的树右子树的零路径长小于左子树,那么就交换左右子树。插入的话,可以看成把一棵只有一个元素的左偏树和另一棵合并。删除的话,合并左右子树就行了。因为树符合堆序性质,所以取最小值的操作也一样。

斜堆就是左偏树的自调整形式,说白了就是简化版。我们不需要维护路径长,每次合并后都交换左右子树,这样均摊的操作时间不变,思想类似于Splay与AVL。具体的实现,少打几行代码就行了。

左偏树的入门题是HDU1512,同时用并查集找爸爸。

我是看这里写的http://www.cnblogs.com/lxf90/archive/2011/04/07/2008180.html

下面是一个关于此题和左偏树的PPT的链接http://oi.imzzl.com/2010/07/610.html

代码:(是斜堆,去掉//就是左偏树了)

 

#include <stdio.h>



#define MAXN 100000



struct node

{

    int dis,l,r,strong;

}tree[MAXN];



int i,j,k,m,n;

int father[MAXN];

int x,y,fx,fy;







void swap(int *x, int *y)

{

    int temp;

    temp = *x;

    *x = *y;

    *y = temp;

}



int find(int x)

{

    if (x != father[x])

        father[x] = find(father[x]);

    return father[x];

}





int merge(int x, int y)

{

    if (x == 0)

        return y;

    if (y == 0)

        return x;

    if (tree[x].strong < tree[y].strong)

        swap(&x, &y);

    tree[x].r = merge(tree[x].r, y);

    int l = tree[x].l;

    int r = tree[x].r;

    father[r] = x;

   // if (tree[l].dis < tree[r].dis)

        swap(&tree[x].l, &tree[x].r);

   // if (tree[x].r == 0)

   //     tree[x].dis = 0;

   // else

   //     tree[x].dis = tree[r].dis + 1;

    return x;

}





int del(int x)

{

    int l,r;

    l = tree[x].l;

    r = tree[x].r;

    father[l] = l;

    father[r] = r;

    tree[x].l = tree[x].r = tree[x].dis = 0;

    return merge(l, r);

}





void fight(int x , int y)

{

    int left,right,now;

    tree[x].strong /= 2;

    tree[y].strong /= 2;

    left = del(x);

    right = del(y);

    left = merge(left, x);

    right = merge(right ,y);

    now = merge(left, right);

    printf("%d\n", tree[now].strong);

}





int main()

{

    while (scanf("%d", &n) == 1)

    {

        for (i = 1; i <= n; i++)

        {

            scanf("%d", &tree[i].strong);

            tree[i].l = tree[i].r = tree[i].dis = 0;

            father[i] = i;

        }

        scanf("%d", &m);

        for (i = 1; i <= m; i++)

        {

            scanf("%d%d", &x, &y);

            fx = find(x);

            fy = find(y);

            if (fx == fy)

                printf("-1\n");

            else fight(fx, fy);

        }

    }

    return 0;

}

 

参考资料:

CLRS

数据结构与算法分析

你可能感兴趣的:(优先队列)