【问题标题】:Checking if 2 people are connected via friends检查 2 个人是否通过朋友联系
【发布时间】:2019-12-09 19:47:14
【问题描述】:

每个子列表都意味着他们是朋友,现在我想创建一个函数def is_connected_via_friendships_with 来检查他们是否通过与另一个人的友谊联系在一起。例如,玛丽是卢卡斯的朋友。她是彼得的朋友,彼得是朱莉的朋友。所以该函数需要为 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

【问题讨论】:

  • 你都试过什么?您可能需要考虑添加您尝试过的代码!
  • 问题在于它是更大代码的一部分并使用其他功能。如果他们有共同的朋友,我知道该怎么做,但我真的找不到合适的方法来解决这个问题。
  • 你可以使用多种方法来解决这个问题,我添加了一个不相交的集合解决方案!

标签: python python-3.x


【解决方案1】:

您基本上必须找出两个朋友是否位于图的相同连通分量中。检查 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()

输出是:

正确
假的

【讨论】:

    【解决方案2】:

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

    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
    

    【讨论】:

    • 不使用break 怎么写?他们说这是不好的做法,我不能用这个。
    • 不确定“他们”是谁,但break 是一个让您的代码干净高效的好工具。这绝不是一个坏习惯。如果您不使用它,则必须使用布尔标志来指示何时结束循环,从而使代码更加混乱。
    • 我的大学是这么说的。我不能使用它是愚蠢的,因为我已经阅读了很多关于它如何非常有用的信息
    猜你喜欢
    • 2012-05-20
    • 1970-01-01
    • 1970-01-01
    • 2020-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-31
    • 1970-01-01
    相关资源
    最近更新 更多