Как перебрать дерево/граф

мне нужно перебрать дерево / график и произвести определенный вывод, но следуя некоторым правилам:

     _ d
   /  / 
  b  c  _e
 /     / |
a     f  g

ожидаемый результат должен быть (порядок не имеет значения):

{'bde', 'bcde', 'abde', 'abcde', 'bdfe', 'bdfge', 'abdfe', ...}

правила:

  1. вершина дерева 'bde' (leftmost_root_children+root+rightmost_root_children) должна всегда присутствовать
  2. левый-правый порядок должен быть сохранен, поэтому, например, комбинации " cb " или " gf " не допускаются.
  3. все пути следуйте слева направо.

мне нужно найти все пути следование этим правилам. К сожалению, у меня нет фона CS, и моя голова взрывается. Любой совет будет полезен.

EDIT: эта структура очень близко представляет мое дерево:

class N():
    """Node"""
    def __init__(self, name, lefts, rights):
        self.name = name
        self.lefts = lefts
        self.rights = rights

tree = N('d', [N('b', [N('a', [], [])], []), N('c', [], [])], 
              [N('e', [N('f', [], []), N('g', [], [])], 
                      [])])

или может быть более читабельным:

N('d', lefts =[N('b', lefts=[N('a', [], [])], rights=[]), N('c', [], [])], 
       rights=[N('e', lefts=[N('f', [], []), N('g', [], [])], rights=[])])

1 ответов


таким образом, это можно рассматривать как комбинацию двух проблем. Мой код ниже будет предполагать N класс а tree структура уже была определена как в вашей постановке проблемы.

первый: учитывая древовидную структуру, подобную вашей, Как вы производите обход по порядку ее узлов? Это довольно простая проблема, поэтому я просто покажу простой рекурсивный генератор, который решает он:

def inorder(node):
    if not isinstance(node, list):
        node = [node]
    for n in node:
        for left in inorder(getattr(n, 'lefts', [])):
            yield left
        yield n.name
        for right in inorder(getattr(n, 'rights', [])):
            yield right

print list(inorder(tree))
# ['a', 'b', 'c', 'd', 'f', 'g', 'e']

второй: теперь у нас есть "правильное" упорядочение узлов, нам Далее нужно выяснить все возможные комбинации из них, что a) поддерживать этот порядок, и b) содержат три элемента "якорь" ('b', 'd', 'e'). Этого можно достичь, используя помощь всегда пригодится itertools библиотека.

основные шаги:

  • определите якорные элементы и разделите список на четыре части вокруг них
  • выяснить все комбинации элементов для каждого раздела (т. е. мощность)
  • возьмите произведение всех таких комбинаций

вот так:

from itertools import chain, combinations
# powerset recipe taken from itertools documentation
def powerset(iterable):
    "powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
    s = list(iterable)
    return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))

def traversals(tree):
    left, mid, right = tree.lefts[0].name, tree.name, tree.rights[0].name
    nodes = list(inorder(tree))
    l_i, m_i, r_i = [nodes.index(x) for x in (left, mid, right)]
    parts = nodes[:l_i], nodes[l_i+1:m_i], nodes[m_i+1:r_i], nodes[r_i+1:]
    psets = [powerset(x) for x in parts]
    for p1, p2, p3, p4 in product(*psets):
        yield ''.join(chain(p1, left, p2, mid, p3, right, p4))

print list(traversals(tree))
# ['bde', 'bdfe', 'bdge', 'bdfge', 'bcde', 'bcdfe', 
#  'bcdge', 'bcdfge', 'abde', 'abdfe', 'abdge', 'abdfge', 
#  'abcde', 'abcdfe', 'abcdge', 'abcdfge']