【问题标题】:Writing nested dictionary (forest) of a huge depth to a text file in BFS style以 BFS 样式将深度嵌套的字典(森林)写入文本文件
【发布时间】:2019-02-03 06:13:47
【问题描述】:

继续我的旧问题: Writing nested dictionary (forest) of a huge depth to a text file

现在我想以 BFS 风格编写森林遍历: 我有一个代表森林(许多非二叉树)的巨大深度字典,我想处理森林并使用森林中的(父亲,儿子)关系序列创建一个文本文件,即给定字典:

{'a': {'b': {'c': {'x': {}}, 'd': {'p': {}}}, 'g': {}, 'f': {}},
 't': {'r': {'o': {}}, 'y': {}}}

生成的文本文件如下所示:

(ROOT,b) (ROOT,g) (ROOT,f) (b,c) (b,d) (c,x) (d,p) \n
(ROOT,r) (ROOT,y) (r,o) \n

请注意,我将森林中的所有根替换为单词“ROOT”。

这是森林的简单可视化:

嵌套字典很大,递归地对其进行迭代会导致内存运行时错误,因此本问题开头链接中的“生成器样式”解决方案将是最好的。

【问题讨论】:

  • 不应该把(a,b)也写入文件吗?
  • @Ajax1234 我认为每个单独的树都占用一个换行符并且忽略根节点
  • @Ajax1234 看到 Joe 的评论,而不是 (a,b) 我写的是 (ROOT,b)

标签: python dictionary tree nested breadth-first-search


【解决方案1】:
d = {'a': {'b': {'c': {'x': {}}, 'd': {'p': {}}}, 'g': {}, 'f': {}}, 't': {'r': {'o': {}}, 'y': {}}}
with open('file', 'w') as f:
    for r, s in d.items():
        q = []
        p = r
        while True:
            for k, v in s.items():
                f.write('(%s,%s) ' % ('ROOT' if p == r else p, k))
                if v:
                    q.append((k, v))
            if not q:
                break
            p, s = q.pop(0)
        f.write('\n')

这个输出:

(ROOT,b) (ROOT,g) (ROOT,f) (b,c) (b,d) (c,x) (d,p) 
(ROOT,r) (ROOT,y) (r,o) 

【讨论】:

  • 您能否更改您的答案以写入输出文件?
  • 当然。我刚刚更新了我的答案,以便它输出到一个名为 file 的文件。
  • 谢谢。您认为您的解决方案比建议的其他解决方案更快吗?
  • @blhsing 这不是广度优先搜索! 看起来太简洁了,难以置信:p... 使用:d={"a":{"h":{"i":{"j":{"k":{}}}},"l":{"m":{"n":{"o":{}}}}}},它会产生:(ROOT,h) (ROOT,l) (l,m) (m,n) (n,o) (h,i) (i,j) (j,k) .
  • @blhsing +1,非常好,您从我的解决方案中删除了整个循环!
【解决方案2】:

要执行breadth-first-search,我们必须保留当前工作节点及其下方的树的列表 - 我选择将这些存储在一个元组中。

例如,当我们在 cd 节点的深度工作时,这个树列表将是:

[('c': {'x': {}}), ('d': {'p': {}})]

现在虽然我们下面还有树 (while len(trees):),我们需要下到我们树下面的层。

第一步显然是重置trees列表,因为我们将生成下一层。

然后我们遍历我们的树列表,并且对于每棵树,我们遍历它的子节点。

以上面的例子为例,在第一次迭代中,节点是'c',子节点是:{'x': {}},我们现在要遍历子节点。因此,在该子循环的第一次迭代中,第一个子节点将是 'x',其子节点(c 的子节点)为空:{}

现在,在这个范围内(节点的子节点),如果子节点有子节点,我们希望将子节点及其子节点(再次作为元组)添加到树列表中。

举个例子,哪里有孩子,当当前节点是b,那么它的一个孩子是c,因为c有孩子,一个元组(c,@987654337 @'s children) 被附加到下一层的树列表中。

最后,不管这个孩子有没有孩子,我们都想在文件的当前行找到我们和他们之间的联系。这是(node, child_node)

差不多就是这样。当然,当我们完成一棵树时,我们需要在文件中写一个换行符。

唯一烦人的细节是写入文件的元组之间的空格问题。如果我们总是在每个元组的末尾连接一个空格,我们最终会在每行的末尾有一个杂散的空格,如下所示,这并不理想。

(ROOT, a)S(a,b)S

(其中S 代表一个空格)

因此,为了弥补这一点,只要我们不是新行中的第一个 (line_first),我们将始终在每个元组 之前 连接一个空格。为此,在每棵树(行)的开头,我们将line_first 标志设置为True,但随后在代码中,我们在第一次迭代时立即将其设置为False(但跳过编写空格),否则(未来的元组)我们在前面写一个空格。

就是这样。这是完整的代码:

the_tree = {'a': {'b': {'c': {'x': {}}, 'd': {'p': {}}}, 'g': {}, 'f': {}},
            't': {'r': {'o': {}}, 'y': {}}}

with open('the_file', 'w') as file:
    for tree in the_tree.values():
        line_first = True
        trees = [('ROOT', tree)]
        while len(trees):
            new_trees = []
            for node, children in trees:
                for child_node, child_children in children.items():
                    if child_children:
                        new_trees.append((child_node, child_children))
                    if line_first: line_first = False
                    else: file.write(' ')
                    file.write(f'({node}, {child_node})')
            trees = new_trees
        file.write('\n')

警告:使用 3.6 版中引入的 f-strings


它会产生预期的输出:

(ROOT, b) (ROOT, g) (ROOT, f) (b, c) (b, d) (c, x) (d, p)
(ROOT, r) (ROOT, y) (r, o)

【讨论】:

    【解决方案3】:

    使用生成器递归遍历结构是最简单的:

    def flatten_forest(forest, write=True):
      def flatten(d, seen = None):
        for a, b in d.items():
          if seen is None:
           yield ('ROOT', a)
          else:
            yield (seen, a)
          if b:
            yield from flatten(b, a)
      if write:
        with open('full_flattened_tree.txt', 'a') as f:
          f.write(' '.join(map(str, flatten(forest)))+'\n')
    
    data = {'a': {'b': {'c': {'x': {}}, 'd': {'p': {}}}, 'g': {}, 'f': {}}, 't': {'r': {'o': {}}, 'y': {}}}
    for i in data.values():
      flatten_forest(i)
    

    文件输出:

    ('ROOT', 'b') ('b', 'c') ('c', 'x') ('b', 'd') ('d', 'p') ('ROOT', 'g') ('ROOT', 'f')
    ('ROOT', 'r') ('r', 'o') ('ROOT', 'y')
    

    这适用于大型词典:

    import random, string, time
    def create_structure(_len, _depth = 5, _count = 0):
     return {string.ascii_lowercase[i]:{} if _depth == _count else create_structure(random.randint(1, 26), _count = _count + 1) for i in range(_len)}
    
    d = create_structure(26)
    c = time.time()
    flatten_forest(d, write=True)
    print(time.time()-c)
    

    输出:

    11.871491193771362
    

    【讨论】:

    • 最终将达到最大递归深度,这不是 BFS :/
    • 但是输出的顺序应该和 BFS 一样。
    • @Codevan 你能解释一下你所说的有序吗?
    • @Ajax1234 在 BFS 中你逐层遍历树,我的输出顺序是正确的
    • @Codevan 那么根不应该是最后一个,好像树是水平遍历的,那么根将是最后一个被计算的?
    猜你喜欢
    • 2019-01-01
    • 2018-05-14
    • 1970-01-01
    • 1970-01-01
    • 2018-06-28
    • 2023-03-19
    • 1970-01-01
    • 1970-01-01
    • 2013-06-17
    相关资源
    最近更新 更多