以BFS样式向文本文件编写深度很大的嵌套字典(森林)

时间:2018-08-28 14:52:41

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

继续我的上一个问题: 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”一词替换了林中的所有根。

这是森林的简单可视化: forest vosualization

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

3 个答案:

答案 0 :(得分:2)

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) 

答案 1 :(得分:1)

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

例如,当我们在cd节点的深度处工作时,此树列表将为:

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

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

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

然后,我们遍历树的列表,并针对每棵树遍历其子树。

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

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

举个例子,在哪里有子节点,当当前节点为b时,其子节点之一为c,由于c有子节点,因此元组为( cc的孩子)将添加到下一层的树列表中。

最后,无论这个孩子有没有孩子,我们都希望在文件中的当前行链接我们和他们之间的链接。这是(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)

答案 2 :(得分:0)

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

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