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

有时候需要合并优先队列,对于二叉堆,只能以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

数据结构与算法分析

posted on 2011-07-08 06:09  oa414  阅读(762)  评论(0编辑  收藏  举报

导航