O(1)Make,Find,Union in Disjoint Sets Data Structure

时间:2010-08-14 21:58:23

标签: algorithm minimum-spanning-tree disjoint-sets disjoint-union

今天,由于this slide的第13页,我与某人就Kruskal最小生成树算法进行了讨论。

演示文稿的作者说,如果我们使用(双重)链表实现不相交集,制作查找的效果将是 O(1分别 O(1)。操作时间 Union(u,v) min(nu,nv),其中 nu nv 是存储u和v的集合的大小。

我说我们可以通过使每个成员的表示指针指向定位器来缩短 Union(u,v) O(1)的时间包含指向集合的实际表示的指针。

在Java中,数据结构如下所示:

class DisjointSet {
    LinkedList<Vertex> list = new LinkedList<Vertex>(); // for holding the members, we might need it for print

    static Member makeSet(Vertex v) {
        Member m = new Member();

        DisjointSet set = new DisjointSet();
        m.set = set;
        set.list.add(m);

        m.vertex = v;

        Locator loc = new Locator();
        loc.representation = m;
        m.locator = loc;

        return m;
    }
}

class Member {
    DisjointSet set;
    Locator locator;
    Vertex vertex;

    Member find() {
        return locator.representation;
    }

    void union(Member u, Member v) { // assume nv is less than nu
        u.set.list.append(v.set.list); // hypothetical method, append a list in O(1)
        v.set = u.set;
        v.locator.representation = u.locator.representation;
    }

}

class Locator {
    Member representation;
}

对于简约代码,我们深表歉意。如果可以这样做,那么每个不相交的设置操作( Make,Find,Union )的运行时间将是 O(1)。但我与之讨论过的那个人看不到改进。我想知道你对此的看法。

此外,各种实现中Find / Union的最快性能是什么?我不是数据结构方面的专家,但通过在互联网上快速浏览,我发现没有恒定时间的数据结构或算法来做到这一点。

2 个答案:

答案 0 :(得分:3)

我的直觉与你的同事一致。你说:

u.set.list.append(v.set.list); // hypothetical method, append a list in O(1)

看起来你的意图是联盟是通过追加来完成的。但是,要实现Union,您必须删除重复项才能将结果作为集合。所以我可以看到一个固定集合大小的O(1)算法,例如......

Int32 set1;
Int32 set2;

Int32 unionSets1And2 = set1 | set2;

但这让我感到作弊。如果您对N的一般情况执行此操作,我不会看到您如何避免某种形式的迭代(或散列查找)。这将使其成为O(n)(或者最好是O(log n))。

仅供参考:我很难跟踪您的代码。在makeSet中,构造一个永远不会转义函数的本地定位器。它看起来不像什么。目前还不清楚你的意图是什么。可能想要编辑和详细说明你的方法。

答案 1 :(得分:3)

使用Tarjan's version of the Union-Find structure(带路径压缩和等级权重联合), m Finds和(n-1)混合联合的序列将在O(m.α(m,n)),其中α(m,n)是Ackermann function的倒数,对于 m n 具有值4.因此,这基本上意味着Union-Find具有最坏情况下的摊销常数操作,但不完全。

据我所知,不可能获得更好的理论复杂性,尽管改进已经带来更好的实际效率。

对于语言理论中使用的不相交集的特殊情况,已经证明线性(即,O(1)中的所有内容)适应可能的 - 主要是通过分组节点在一起---但这些改进无法转化为一般问题。另一方面,有一个类似的核心思想已被用于制作最小生成树(Chazelle算法)的O(n)算法,并取得了巨大的成功和独创性。

所以你的代码不正确。错误是Moron指出的:当你创建两个集合的联合时,你只更新每个列表的前导的“表示”,而不是更新所有其他元素 - 同时在find函数中假设每个元素直接知道它的代表性。