要执行breadth-first-search,我们必须保留当前工作节点及其下方的树的列表 - 我选择将这些存储在一个元组中。
例如,当我们在 c 和 d 节点的深度工作时,这个树列表将是:
[('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)