查找树中元素的完整路径

时间:2019-04-02 12:19:33

标签: python recursion tree

我想在树中找到元素的完整路径。元素可以位于几个地方。

tree

我当前的代码:

levels = [
    {"L3A": ["L4A"]},
    {"L3B": ["L4B"]},
    {"L3C": ["L4C"]},
    {"L1": ["L2", "L4A"]},
    {"L2": ["L3A", "L3B", "L3C"]}
]

def get_level(name):
    tree = []
    recursive(name, tree)
    return tree

def recursive(name, tree):
    for level in levels:
        for k, v in level.items():
            if name in v:
                tree.append(k)
                recursive(k, tree)

levl = get_level("L4A")
print(levl)

结果

是:['L3A', 'L2', 'L1', 'L1']

想要:[['L3A', 'L2', 'L1'], ['L1']]

最终想要:

L4A in L1 > L2 > L3A

L4A in L1 

您能给我一些建议如何更改它吗?

2 个答案:

答案 0 :(得分:0)

反转关联图,然后应用标准图搜索,例如。 DFS。

答案 1 :(得分:0)

为什么L1在您的列表中出现两次?因为您有两条通往L4A的路径:L1 -> L2 -> L3A -> L4AL1 -> L4A,但是只有一个path变量可以存储这些路径。由于您使用了一种反向DFS,因此您具有以下级别:L4 -> L3A -> L2 -> L1,然后是L4 -> L1

让我们尝试阐述一种算法。您正在处理图形(如果您添加根,您会得到一棵树),因此我将使用通常的词汇:级别是“节点”,级别之间的路径是“边缘”。这是进行操作的好方法:

  • 已给定:节点N
  • 找到所有节点P,以使边缘P-N存在并存储路径。
  • 对于每个P,找到所有节点Q,以使边缘P-Q存在并存储路径。
  • 一旦节点edge不再有Q,即当前path最大,则返回path

作为真正的算法,它缺乏精确度。让我们关注:

GIVEN: a node N
let paths_to_explore = [N]
while paths_to_explore is not empty:
    for every path_to_explore:
        try to add a node at the beginning of path_to_explore
        if it fails, yield path_to_explore

在获得代码之前,请注意,图形的表示形式不是两种常用的表示形式。就您而言,您具有边列表,但是字典from_node -> [to_nodes]更好:

edges = {
    "L3A": {"L4A"},
    "L3B": {"L4B"},
    "L3C": {"L4C"},
    "L1": {"L2", "L4A"},
    "L2": {"L3A", "L3B", "L3C"},
}

这使边缘上的迭代更容易:

for from_node, to_nodes in edges.items():
    # do something with nodes

现在,代码:

def find_reverse_path(name):
    paths = []
    paths_to_explore = [[name]]
    while paths_to_explore:
        path = paths_to_explore.pop() # next!
        to_node = path[0] # the HEAD of the current path
        expanded = False
        for from_node, to_nodes in edges.items():
            if to_node in to_nodes: # there's an edge to the HEAD
                new_path_to_explore = [from_node] + path # new path = from_node + old path
                paths_to_explore.append(new_path_to_explore) # add it to the exploration list
                expanded = True # this path was expanded

        if not expanded: # the path is maximal
            paths.append(path) # use yield if you want to create a generator

    return paths

print(find_reverse_path("L4A"))

输出:

[['L1', 'L4A'], ['L1', 'L2', 'L3A', 'L4A']]

这是一个迭代的DFS。 (我想我们很难知道是否在递归DFS中扩展了路径。)

看看这两行,它们包含“技巧”:

new_path_to_explore = [from_node] + path # new path = from_node - old path
paths_to_explore.append(new_path_to_explore) # add it to the exploration list

请注意,new_path_to_explorepath副本,这意味着我们不只是向paths[-1]添加节点(就地)。这是为什么?看一下第一步:

1. paths = [[L4A]]
2. paths = [], path = [L4A]
3. append L1-L4A to paths, then append L3A-L4A to paths
4. paths = [[L1, L4A], [L3A, L4A]]
5. paths = [[L1, L4A]], path = [L3A, L4A]
...

如果不进行复制,并且在当前路径的开头找到多个边,则将在步骤4 paths = [[L3A, L1, L4]]中执行。那几乎就是您在代码中遇到的相同问题。