检查是否有2个人通过朋友连接

时间:2019-12-09 19:47:14

标签: python python-3.x

每个子列表都意味着他们是朋友,现在我想创建一个函数def is_connected_via_friendships_with,以检查他们是否通过友谊与其他人联系在一起。例如,玛丽(Marie)和卢卡斯(Lucas)是朋友。她是Peter的朋友和Peter和Julie的朋友。因此,该函数需要为Marie和Julie返回True。另请注意,我不能为此使用import。预先谢谢你。

network = {'friendships' :[['Marie', 'Lucas'], ['Lucas', 'Patsy'], ['Emma', 'Lucas'], ['Emma', 'Kevin'], ['Peter', 'Emma'], ['Peter', 'Lucas'], ['Peter', 'Julie'], ['Suzy', 'Tobias']]}
print (is_connected_via_friendships_with(network, 'Marie', 'Julie')) # true
print (is_connected_via_friendships_with(network, 'Julie', 'Tobias')) # false
print (is_connected_via_friendships_with(network, 'Julie', 'Frank'))  # false

2 个答案:

答案 0 :(得分:3)

基本上,您必须确定两个朋友是否位于该图的相同连接组件中。检查2个节点是否在同一连接的组件中的多种方法。您可以将不交集用于此问题的优化实施。 Here

中使用的不相交集实现

代码如下:

class DisjointSet:
    def __init__(self, vertices, parent):
        self.vertices = vertices
        self.parent = parent

    def find(self, item):
        if self.parent[item] == item:
            return item
        else:
            return self.find(self.parent[item])

    def union(self, set1, set2):
        root1 = self.find(set1)
        root2 = self.find(set2)
        self.parent[root1] = root2

    def is_in_same_set(self, fa, fb):
        return self.find(fb) == self.find(fa)

def get_vertices(network):
    myset = set()
    for a,b in network['friendships']:
        myset.add(a)
        myset.add(b)
    return list(myset)


def is_connected_via_friendships_with(network, fa,fb):
    return network.is_in_same_set(fa,fb)

def main():

    network = {'friendships' :[['Marie', 'Lucas'], ['Lucas', 'Patsy'], ['Emma','Lucas'], ['Emma', 'Kevin'], ['Peter', 'Emma'], ['Peter', 'Lucas'], ['Peter', 'Julie'],     ['Suzy', 'Tobias']]}

    vertices = get_vertices(network)
    parent = {}

    for v in vertices:
        parent[v] = v

    ds = DisjointSet(vertices, parent)

    for a,b in network['friendships']:
        ds.union(a,b)

    print(is_connected_via_friendships_with(ds, 'Marie', 'Julie'))
    print(is_connected_via_friendships_with(ds, 'Peter', 'Suzy'))
main()

输出为:

  

真实
  错误

答案 1 :(得分:1)

这是一个经典的connected component问题。由于友谊是双向的,因此将子列表的列表转换为一组可哈希化的冻结集作为候选池更为有效,这样,对于每个起始顶点,您可以通过遍历冻结集集中的候选对象来找到连接的组件,从而找到与当前组件集相交的冻结集,然后将其从候选池中删除,直到池中没有更多候选:

pool = set(map(frozenset, network['friendships']))
groups = []
while pool:
    groups.append(set(pool.pop()))
    while True:
        for candidate in pool:
            if groups[-1] & candidate:
                groups[-1] |= candidate
                pool.remove(candidate)
                break
        else:
            break

groups将变为:

[{'Patsy', 'Emma', 'Peter', 'Marie', 'Julie', 'Kevin', 'Lucas'}, {'Suzy', 'Tobias'}]

然后将集列表转换为以每个名称作为有效查找关键字的集字典是很简单的:

connected = {name: group for group in groups for name in group}
def is_connected_via_friendships_with(name1, name2):
    return name2 in connected.get(name1, ())

这样:

print(is_connected_via_friendships_with('Marie', 'Julie'))
print(is_connected_via_friendships_with('Julie', 'Tobias'))
print(is_connected_via_friendships_with('Julie', 'Frank'))

输出:

True
False
False