python - 如何从邻接列表构建嵌套树结构?
问题描述
考虑到我有:
- 命名为相邻键(子 - 父)的列表
A
- 一个名为
Tree
存储自己的节点键(整数)和子节点(类)的树类
A = [(61, 66), (50, 61), (68, 61), (33, 61), (57, 66), (72, 66), (37, 68), (71, 33), (6, 50), (11, 37), (5, 37)]
class Tree:
def __init__(self, node, *children):
self.node = node
if children: self.children = children
else: self.children = []
def __str__(self):
return "%s" % (self.node)
def __repr__(self):
return "%s" % (self.node)
def __getitem__(self, k):
if isinstance(k, int) or isinstance(k, slice):
return self.children[k]
if isinstance(k, str):
for child in self.children:
if child.node == k: return child
def __iter__(self): return self.children.__iter__()
def __len__(self): return len(self.children)
如何构建一个 Tree 对象,使其根据邻接关系封装所有内部树?(如下所示)
t = Tree(66,
Tree(72),
Tree(57),
Tree(61,
Tree(33,
Tree(71)),
Tree(50,
Tree(6)),
Tree(68,
Tree(37,
Tree(11), Tree(5)))))
我正在考虑以递归方式创建树,但我无法弄清楚如何正确遍历和填充它。这是我失败的尝试:
from collections import defaultdict
# Create a dictionary: key = parent, values = children
d = defaultdict(list)
for child, parent in A:
d[parent].append(child)
# Failed attempt
def build_tree(k):
if k in d:
tree = Tree(k, d[k]) #1st issue: should input a Tree() as 2nd parameter
for child in d[k]:
build_tree(child) #2nd issue: should populate tree, not iterate recursively over children keys
#I know that the root node is 66.
full_tree = build_tree(66)
解决方案
您在这段代码中提到了两个问题:
tree = Tree(k, d[k]) #1st issue: should input a Tree() as 2nd parameter
for child in d[k]:
build_tree(child) #2nd issue: should populate tree, not iterate recursively over children keys
您可以通过本质上将for
循环移动到第二个参数中来解决它们,以列表理解的形式并飞溅该列表,使它们成为参数。然后确保您的递归函数返回创建的树:
return Tree(k,
*[build_tree(child) for child in d[k]]
)
更多创意
与您的问题无关,但这里有一些您可以使用的更多想法。
建议使您的代码成为可以
A
作为参数传递的函数,这样字典的范围也只是该函数的本地范围,并且不会乱扔全局范围。由于此功能与类密切相关
Tree
,因此最好将其定义为类中的静态或类方法。当您拥有树的 (child, parent) 元组时,这些元组会隐式定义哪个节点是根,因此您可以省略将文字 66 传递给您的函数。该函数应该能够自己找出哪个是根。在创建字典时,它还可以收集哪些节点有父节点。根是不在该集合中的节点。
所以把所有这些放在一起你会得到这个:
from collections import defaultdict
class Tree:
def __init__(self, node, *children):
self.node = node
self.children = children if children else []
def __str__(self):
return "%s" % (self.node)
def __repr__(self):
return "%s" % (self.node)
def __getitem__(self, k):
if isinstance(k, int) or isinstance(k, slice):
return self.children[k]
if isinstance(k, str):
for child in self.children:
if child.node == k:
return child
def __iter__(self):
return self.children.__iter__()
def __len__(self):
return len(self.children)
@classmethod
def from_pairs(Cls, pairs):
# Turn pairs into nested dictionary
d = defaultdict(list)
children = set()
for child, parent in pairs:
d[parent].append(child)
# collect nodes that have a parent
children.add(child)
# Find root: it does not have a parent
root = next(parent for parent in d if parent not in children)
# Build nested Tree instances recursively from the dictionary
def subtree(k):
return Cls(k, *[subtree(child) for child in d[k]])
return subtree(root)
# Sample run
A = [(61, 66), (50, 61), (68, 61), (33, 61), (57, 66), (72, 66), (37, 68), (71, 33), (6, 50), (11, 37), (5, 37)]
tree = Tree.from_pairs(A)
推荐阅读
- typescript - 如何使控制台日志一起显示错误而不是单独显示?
- git - git log 在 git fetch 之后不跟踪更新(在 --no-checkout clone 中)
- excel - 添加新值时向下移动单元格中的值
- maven - Maven.I 可以在 IntelliJ 中运行我的 JUnit 测试,但 maven 只报告执行了 0 个测试
- java - 如何在 TextView 中显示带有参数的 HTML 字符串资源?
- java - java.lang.ArrayIndexOutOfBoundsException:简单的for循环打印数组
- python - 尝试从稀疏矩阵制作图:没有足够的值来解包(预期 2,得到 0)
- python - 计算长度为 5 且恰好有两个重复数字的数字的数量
- java - 无法从字符串“2020-12-18 16:04:07-06”反序列化“java.time.LocalDateTime”类型的值
- wordpress - 根据序列化的元值获取帖子