【问题标题】:Find all cycles in a dictionary containing more than 1 cycle在包含超过 1 个循环的字典中查找所有循环
【发布时间】:2021-08-30 03:13:16
【问题描述】:

给定以下循环字典:

{'E': ['F'], 'F': ['C', 'G'], 'C': ['E'], 'G': ['H'], 'H': ['I'], 'I': ['J', 'D'], 'J': ['D'], 'D': ['E']}

其中键代表父节点,值是它指向的所有子节点(这表示有向图)。

可以看出有3个循环:

  1. E -> F -> C
  2. E -> F -> G -> H -> I -> J -> D
  3. E -> F -> G -> H -> I -> D

我正在尝试找出一种能够提取所有循环的方法或函数,以便我可以输出或返回三个循环:

EFC

EFGHIJD

EFGHID

我正在寻找 Python 中的解决方案!谢谢:)

【问题讨论】:

  • 请告诉我们您的尝试。这不是一个家庭作业论坛,你可以发布一个问题,有人会给你答案。
  • 我尝试使用 for 循环来遍历字典并打印出键或键的值,但我真的不知道如何将它们组合为循环。

标签: python dictionary nodes cycle directed-graph


【解决方案1】:

获取一个起始节点的所有周期的一种方法是:

def cycles(graph, start):
    results = []
    paths = [(start,)]
    while paths:
        path = paths.pop()
        childs = set(graph.get(path[-1], []))
        if start in childs:
            results.append(path)
        paths.extend(
                  (path + (child,))
                  for child in childs.difference(path)
              )
    return results

使用您的图表和起始节点E,您将获得(print(cycles(g, 'E'))g 您的字典):

[('E', 'F', 'G', 'H', 'I', 'D'),
 ('E', 'F', 'G', 'H', 'I', 'J', 'D'),
 ('E', 'F', 'C')]

如果您想要一个图表中的所有循环,您将不得不做更多的工作:一个单一的起始节点通常不会导致一个图表的所有循环。实现这一目标的一种方法是遍历所有节点,应用cycles,然后收集所有结果:

result = [cycle for node in g for cycle in cycles(g, node)]
[('E', 'F', 'G', 'H', 'I', 'J', 'D'),
 ('E', 'F', 'G', 'H', 'I', 'D'),
 ('E', 'F', 'C'),
 ('F', 'G', 'H', 'I', 'J', 'D', 'E'),
 ('F', 'G', 'H', 'I', 'D', 'E'),
 ('F', 'C', 'E'),
 ('C', 'E', 'F'),
 ('G', 'H', 'I', 'J', 'D', 'E', 'F'),
 ('G', 'H', 'I', 'D', 'E', 'F'),
 ('H', 'I', 'J', 'D', 'E', 'F', 'G'),
 ('H', 'I', 'D', 'E', 'F', 'G'),
 ('I', 'J', 'D', 'E', 'F', 'G', 'H'),
 ('I', 'D', 'E', 'F', 'G', 'H'),
 ('J', 'D', 'E', 'F', 'G', 'H', 'I'),
 ('D', 'E', 'F', 'G', 'H', 'I'),
 ('D', 'E', 'F', 'G', 'H', 'I', 'J')]

但这种方式会导致一些冗余:('E', 'F', 'C')('C', 'E', 'F')( 'F', 'C', 'E') 本质上是相同的(等等)。如果你想摆脱它,你可以这样做:

results = []
d = {node: set(g.get(node, [])) for node in g}
nodes = list(d)
while nodes:
    node = nodes.pop()
    results.extend(cycles(d, node))
    del d[node]
    for other_node in d:
        d[other_node].discard(node)

print(results):

[('D', 'E', 'F', 'G', 'H', 'I'),
 ('D', 'E', 'F', 'G', 'H', 'I', 'J'),
 ('C', 'E', 'F')]

【讨论】:

    【解决方案2】:

    您可以使用递归生成器函数:

    d = {'E': ['F'], 'F': ['C', 'G'], 'C': ['E'], 'G': ['H'], 'H': ['I'], 'I': ['J', 'D'], 'J': ['D'], 'D': ['E']}
    def get_cycles(n, o = None, c = []):
       if n == o: #check if current node being examined is the same as the start
          yield c #if the condition is met, then a cycle is found and the path is yielded back
       else:
          #get the children of the current node from d and iterate over only those that have not been encountered before (except if the node is the same as the start)
          for i in filter(lambda x:x not in c or x == o, d.get(n, [])):
             #recursively find the children of this new node `i`, saving the running path (c + [n])
             yield from get_cycles(i, o = n if o is None else o, c = c + [n])
    
    print(list(get_cycles('E')))
    print(list(get_cycles('D')))
    

    输出:

    [['E', 'F', 'C'], ['E', 'F', 'G', 'H', 'I', 'J', 'D'], ['E', 'F', 'G', 'H', 'I', 'D']]
    [['D', 'E', 'F', 'G', 'H', 'I', 'J'], ['D', 'E', 'F', 'G', 'H', 'I']]
    

    【讨论】:

    • 哇,很棒的解决方案,谢谢!你介意解释一下吗?我很难理解。如果您知道一种更简单/更易于阅读的方法,请分享!谢谢
    • @Yasmine 很高兴为您提供帮助!请查看我编辑中的 cmets,如果您有任何其他问题,请告诉我。
    猜你喜欢
    • 2018-05-15
    • 1970-01-01
    • 2020-11-06
    • 1970-01-01
    • 2020-10-11
    • 2020-02-20
    • 1970-01-01
    • 2019-05-13
    • 1970-01-01
    相关资源
    最近更新 更多