继续我的上一个问题: 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”一词替换了林中的所有根。
嵌套字典很大,并且递归对其进行迭代会导致内存运行时错误,因此,如在此问题开头的链接中所述的“生成器样式”解决方案将是最好的。
答案 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,我们必须保留当前工作节点及其下面的树的列表-我选择将它们存储在元组中。
例如,当我们在c
和d
节点的深度处工作时,此树列表将为:
[('c': {'x': {}}), ('d': {'p': {}})]
现在而下面还有几棵树(while len(trees):
),我们需要下到树的下一层。
第一步显然是重置trees
列表,因为我们将生成下一层。
然后,我们遍历树的列表,并针对每棵树遍历其子树。
因此,以上面的示例为例,在第一次迭代中,节点为'c'
,子节点为{'x': {}}
,现在我们要遍历子节点。因此,在该子项循环的第一次迭代中,第一个子节点将为'x'
,并且其子项(c
的子项的子项)为空:{}
。
现在,在这个范围(节点的孩子的孩子)中,如果孩子有孩子,我们想将孩子及其孩子(再次作为元组)添加到树列表中。
举个例子,在哪里有子节点,当当前节点为b
时,其子节点之一为c
,由于c
有子节点,因此元组为( c
,c
的孩子)将添加到下一层的树列表中。
最后,无论这个孩子有没有孩子,我们都希望在文件中的当前行链接我们和他们之间的链接。这是(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