【问题标题】:How to properly implement disjoint set data structure for finding spanning forests in Python?如何正确实现不相交集数据结构以在 Python 中查找跨越森林?
【发布时间】:2020-01-31 12:49:28
【问题描述】:

最近在尝试实现google kickstater 2019编程题的解决方案,按照分析说明尝试实现Round E的Cherries Mesh。 这是问题和分析的链接。 https://codingcompetitions.withgoogle.com/kickstart/round/0000000000050edb/0000000000170721

这是我实现的代码:

t = int(input())
for k in range(1,t+1):
    n, q = map(int,input().split())
    se = list()
    for _ in range(q):
        a,b = map(int,input().split())
        se.append((a,b))
    l = [{x} for x in range(1,n+1)]
    #print(se)
    for s in se:
        i = 0
        while ({s[0]}.isdisjoint(l[i])):
            i += 1
        j = 0
        while ({s[1]}.isdisjoint(l[j])):
            j += 1
        if i!=j:
            l[i].update(l[j])
            l.pop(j)
        #print(l)
    count = q+2*(len(l)-1)
    print('Case #',k,': ',count,sep='')



这通过了示例用例,但没有通过测试用例。据我所知,这应该是正确的。我做错了吗?

【问题讨论】:

  • 您的不相交集实现非常缓慢,但看起来是正确的。但是,您计算的计数不正确。应该是count = n + len(l) - 2
  • 有没有一种有效的方法来实现不相交的数据结构?用蟒蛇。或者我应该依赖像 cpp 或 java 这样的编译语言吗?
  • 当然你可以在 python 中非常有效地做到这一点。我不记得在 SO 上看到过一个非常好的例子,所以如果你愿意,我会在几个小时内给出一个答案。既然您正在练习,也许您想先尝试遵循维基百科的文章:en.wikipedia.org/wiki/Disjoint-set_data_structure

标签: python algorithm data-structures disjoint-sets disjoint-union


【解决方案1】:

您得到的答案不正确,因为您计算的计数不正确。需要n-1 边将n 节点连接成一棵树,其中num_clusters-1 必须是红色的。

但如果你解决了这个问题,你的程序仍然会很慢,因为你的集合实现不相交。

谢天谢地,几乎可以用任何编程语言在单个数组/列表/向量中实现一个非常有效的不相交集合数据结构。这是python中的一个不错的选择。我的盒子上有 python 2,所以我的打印和输入语句与你的有点不同:

# Create a disjoint set data structure, with n singletons, numbered 0 to n-1
# This is a simple array where for each item x:
# x > 0 => a set of size x, and x <= 0 => a link to -x

def ds_create(n):
    return [1]*n

# Find the current root set for original singleton index

def ds_find(ds, index):
    val = ds[index]
    if (val > 0):
        return index
    root = ds_find(ds, -val)
    if (val != -root):
        ds[index] = -root # path compression
    return root

# Merge given sets. returns False if they were already merged

def ds_union(ds, a, b):
    aroot = ds_find(ds, a)
    broot = ds_find(ds, b)
    if aroot == broot:
        return False
    # union by size
    if ds[aroot] >= ds[broot]:
        ds[aroot] += ds[broot]
        ds[broot] = -aroot
    else:
        ds[broot] += ds[aroot]
        ds[aroot] = -broot
    return True

# Count root sets

def ds_countRoots(ds):
    return sum(1 for v in ds if v > 0)

#
# CherriesMesh solution
#
numTests = int(raw_input())
for testNum in range(1,numTests+1):
    numNodes, numEdges = map(int,raw_input().split())
    sets = ds_create(numNodes)
    for _ in range(numEdges):
        a,b = map(int,raw_input().split())
        print a,b
        ds_union(sets, a-1, b-1)
    count = numNodes + ds_countRoots(sets) - 2
    print 'Case #{0}: {1}'.format(testNum, count)
    

【讨论】:

  • 这个不相交的集合实现真是太棒了。
  • 错字了? root = ds_find(-val)
  • 对,我要传ds
【解决方案2】:

两个问题:

  • 检查边是否连接两个不相交的集合,如果没有则连接它们的算法效率低下。 Disjoint-Set data structure 上的 Union-Find 算法效率更高
  • 最终计数不依赖于黑边的原始数量,因为这些黑边可能有循环,因此其中一些不应该被计算在内。而是计算总共有多少边(无论颜色如何)。由于解表示最小生成树,边数为n-1。从中减去您拥有的不相交集的数量(就像您已经做过的那样)。

我还建议使用有意义的变量名。代码更容易理解。 tqs 等单字母变量不是很有帮助。

有几种方法可以实现 Union-Find 功能。在这里,我定义了一个具有这些方法的 Node 类:

# Implementation of Union-Find (Disjoint Set)
class Node:
    def __init__(self):
        self.parent = self
        self.rank = 0

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

    def union(self, other):
        node = self.find()
        other = other.find()
        if node == other:
            return True # was already in same set
        if node.rank > other.rank:
            node, other = other, node
        node.parent = other
        other.rank = max(other.rank, node.rank + 1)
        return False # was not in same set, but now is

testcount = int(input())
for testid in range(1, testcount + 1):
    nodecount, blackcount = map(int, input().split())
    # use Union-Find data structure
    nodes = [Node() for _ in range(nodecount)]
    blackedges = []
    for _ in range(blackcount):
        start, end = map(int, input().split())
        blackedges.append((nodes[start - 1], nodes[end - 1]))

    # Start with assumption that all edges on MST are red:
    sugarcount = nodecount * 2 - 2
    for start, end in blackedges:
        if not start.union(end): # When edge connects two disjoint sets:
            sugarcount -= 1 # Use this black edge instead of red one

    print('Case #{}: {}'.format(testid, sugarcount))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-01-20
    • 1970-01-01
    • 2021-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多