在Python交替实现中的Disjoint-Set林

时间:2012-02-28 19:20:56

标签: python set disjoint-sets

我在Python中实现了一个不相交的集合系统,但我已经碰壁了。我正在为系统使用树实现,并且正在为系统实现Find(),Merge()和Create()函数。

我正在实施排名系统和路径压缩以提高效率。

问题在于,这些函数必须将一组不相交的集合作为参数,从而使遍历变得困难。

class Node(object):
    def __init__(self, value):
        self.parent = self
        self.value = value
        self.rank = 0

def Create(values):
    l = [Node(value) for value in values]
    return l

Create函数接受一个值列表并返回包含相应数据的单个节点列表。

我认为Merge函数看起来与此类似,

def Merge(set, value1, value2):
    value1Root = Find(set, value1)
    value2Root = Find(set, value2)
    if value1Root == value2Root:
        return
    if value1Root.rank < value2Root.rank:
        value1Root.parent = value2Root
    elif value1Root.rank > value2Root.rank:
        value2Root.parent = value1Root
    else:
        value2Root.parent = value1Root
        value1Root.rank += 1

但我不确定如何实现Find()函数,因为需要将节点列表和值(不仅仅是一个节点)作为参数。查找(设置,值)将成为原型。

我理解当Node作为Find(x)的参数时如何实现路径压缩,但这种方法让我失望。

非常感谢任何帮助。谢谢。

编辑澄清。

3 个答案:

答案 0 :(得分:2)

当您意识到操作union和find也可以作为不相交的set林类的方法而不是单个不相交的集合实现时,这种数据结构的实现变得更加简单。

如果您可以阅读C ++,那么请查看my take on the data structure;它隐藏了来自外部世界的实际集合,仅将它们表示为API中的数字索引。在Python中,它将类似于

class DisjSets(object):
    def __init__(self, n):
        self._parent = range(n)
        self._rank = [0] * n

    def find(self, i):
        if self._parent[i] == i:
            return i
        else:
            self._parent[i] = self.find(self._parent[i])
            return self._parent[i]

    def union(self, i, j):
        root_i = self.find(i)
        root_j = self.find(j)
        if root_i != root_j:
            if self._rank[root_i] < self._rank[root_j]:
                self._parent[root_i] = root_j
            elif self._rank[root_i] > self._rank[root_j]:
                self._parent[root_j] = root_i
            else:
                self._parent[root_i] = root_j
                self._rank[root_j] += 1

(未经测试。)

如果您选择不遵循此路径,则代码的客户端确实必须了解NodeFind必须采用Node参数。

答案 1 :(得分:0)

显然merge函数应该应用于节点对。

所以find函数应该采用单节点参数,如下所示:

def find(node):
    if node.parent != node:
        node.parent = find(node.parent)
    return node.parent

wikipedia has pseudocode也很容易转换为python。

答案 2 :(得分:0)

始终在项目上完成查找。查找(项目)定义为返回项目所属的集合。因此合并不能占用节点,合并总是需要两个项目/集合。合并或联合(item1,item2)必须首先找到(item1)和find(item2),它们将返回每个属于的集合。之后,必须将由向上树表示的较小集合添加到较高的集合中。发出查找时,始终回溯路径并压缩它。

经过测试的路径压缩实现是here