在Python中遍历一个不寻常的树

时间:2010-01-11 15:51:04

标签: python tree traversal

我有一个不寻常的树形数组:

[[0, 1], [1, 2], [2, 3], [2, 4], [2, 5], [5, 6], 
 [4, 6], [3, 6], [0, 7], [7, 6], [8, 9], [9, 6]]

数组的每个元素都是一对,这意味着第二个元素是第一个的跟随者,例如:

[0, 1] - 0 is followed by 1
[1, 2] - 1 is followed by 2

我正在尝试提取这样的数组:

0 1 2 3 6    
0 1 2 4 6    
0 1 2 5 6
0 7 6
8 9 6

我无法编写强大的遍历来提取所有可能的路径。我怎么能用Python做到这一点?

8 个答案:

答案 0 :(得分:4)

你可以使用递归生成器函数来完成它。我假设树中的根节点始终位于原始列表中的所有子节点之前。

tree = [[0, 1], [1, 2], [2, 3], [2, 4], [2, 5], [5, 6], [4, 6], [3, 6],
        [0, 7], [7, 6], [8, 9], [9, 6]]

paths = {}
for t in tree:
    if t[0] not in paths: paths[t[0]] = []
    paths[t[0]].append(tuple(t))

used = set()

def get_paths(node):
    if node[1] in paths:
        for next_node in paths[node[1]]:
            used.add(next_node)
            for path in get_paths(next_node):
                yield [node[0]] + path
    else:
        yield [node[0], node[1]]

for node in tree:
    if tuple(node) in used: continue
    for path in get_paths(node):
        print path

输出:

[0, 1, 2, 3, 6]
[0, 1, 2, 4, 6]
[0, 1, 2, 5, 6]
[0, 7, 6]
[8, 9, 6]

说明:首先,我构建一个包含每个节点的所有可能路径的列表。然后,对于我尚未使用的每个节点,我假设它是一个根节点,并递归地查找从哪个路径引出。如果没有从任何节点找到路径,它就是一个叶子节点,我停止递归并返回找到的路径。

如果关于节点顺序的假设不成立,那么首先必须找到所有根节点的集合。这可以通过查找在任何连接中未显示为第二个节点的所有节点来完成。

答案 1 :(得分:2)

根据我对您的问题的理解,看起来您有一组父子关系作为描述tree的对列表。你似乎遇到麻烦,认为它有一个像链表一样的结构。与链表不同,树是一种更通用的形式,它可以有多个节点“跟随”被称为其子节点的给定节点。

最简单的方法是先构建树,然后从根遍历。定义一个具有两个字段的Node类,一个用于节点的值,另一个用于子节点列表。然后迭代列表中的项目,将每对的第二个元素添加到与该对的第一个元素对应的节点的子列表中。 构建树之后,使用递归打印函数打印当前节点并在其子节点上调用它自己(如果有的话)。在根节点上调用该函数应该打印整个树。

我会发布一些代码,但这看起来很像家庭作业。上面的解释应该足以开始。

答案 2 :(得分:2)

我能想到的最简单的方法是构建一个包含给定父级所有可能子级的字典,如下所示:

d = {}

for parent, child in tree:
    try:
        d[parent].append(child)
    except KeyError:
        d[parent] = [child]

with tree = [[0,1],[1,2],[2,3],[2,4],[2,5],[5,6],[4,6],[ 3,6],[0,7],[7,6],[8,9],[9,6]], 这会产生:

{0: [1, 7],
 1: [2],
 2: [3, 4, 5],
 3: [6],
 4: [6],
 5: [6],
 7: [6],
 8: [9],
 9: [6]}

现在可以像这样以递归方式遍历树:

def printPaths(d, currentPath):
    if currentPath[-1] not in d:
        print currentPath # last node can't possibly be a parent, so stop
    else:
        for child in d[currentPath[-1]]:
            printPaths(d, currentPath + [child])


for root in d:
    printPaths(d, [root])

我没有测试过递归,但它应该给你一个想法:)

答案 3 :(得分:1)

你走了。不是地球上最好的代码,但它有效:

inputValues = [[0, 1], [1, 2], [2, 3], [2, 4], [2, 5], [5, 6], [4, 6], [3, 6], [0, 7], [7, 6], [8, 9], [9, 6]]

tree = {}
numberOfChildren = {}
for (f, t) in inputValues:
  if not tree.has_key(f):
    tree[f] = []
  tree[f].append(t)
  if not numberOfChildren.has_key(t):
    numberOfChildren[t] = 0
  numberOfChildren[t] += 1

roots = [c for c in tree if c not in numberOfChildren]
permutations = []

def findPermutations(node, currentList):
  global tree
  global permutations
  if not tree.has_key(node):
    permutations.append(currentList)
    return
  for child in tree[node]:
    l = list()
    l.extend(currentList)
    l.append(child)
    findPermutations(child, l)

for r in roots:
  findPermutations(r, [r])

print permutations

答案 4 :(得分:0)

看看这个问题,似乎最好的方法可能是在几次迭代中向后构建数组。我的想法是这样的,但请注意我们必须假设这是一棵树,所以叶子只能使用一次:

  1. 让数组=成对列表
  2. 直到数组中的每个数组都是叶子:
    1. 如果数组是叶子(最后一个元素不是数组中任何数组中的第一个元素):
      1. 对于数组中的每个数组,查看是否可以将叶子附加到其末尾
      2. 附加到所有可能的数组后,删除叶
  3. 显然你必须做一些工作才能把它变成代码,但这是一个粗略的想法。

答案 5 :(得分:0)

您可以使用以下页面中的find_all_paths函数: http://www.python.org/doc/essays/graphs/

要使用此功能,您需要对图表进行两次小修改。首先,循环遍历边缘列表并创建图形的新表示,如:

    graph = {0: [1, 7],
             1: [2],
             2: [3, 4, 5],
             ...}
其次创建一个超级链接(在您的示例中,您可以将其称为10)并附加所有顶点,没有从它们引出的边缘到此新节点。

然后你可以调用函数find_all_paths(graph, 0, 10)来找到所有这些路径。

答案 6 :(得分:0)

从所有可能的起始节点生成所有最长的路径:

tree = [[0, 1], [1, 2], [2, 3], ...]

dtree = {}
for (k, v) in tree:
   dtree.setdefault(k, []).append(v)

parts = [[root] for root in range(10)]

while parts:
   path = parts.pop(0)
   if path[-1] in dtree:
      for n in dtree[path[-1]]:
         parts.append(path + [n])
   else:
      print path

如果它只应生成不属于从其他节点开始的不同的更长路径的路径,则需要将parts初始化为[p[1] for p in tree]中未包含的所有节点。如果你想要所有路径,而不仅仅是最长的路径,那么在while循环的每次迭代中都应该有一个打印。

答案 7 :(得分:0)

以下工作 - 从root开始生成树。 根被认为是没有父节点的节点。

import operator
def genpaths(data):
    # Initialize dictionary
    ddata = {}
    for item in data:
        ddata.setdefault(item[0], []).append(item[1])
    def genpath(root):
        "Generate paths starting with root"
        if root not in ddata:
            yield (root, )
        else:
            for child in ddata[root]:
                for path in genpath(child):
                    yield (root, ) + path

    for root in set(ddata.keys()) - set(reduce(operator.add, ddata.values())):
        for path in genpath(root):
            print path