2009-10-23 2 views
8

Каков наилучший способ посетить все узлы связанного дерева (все узлы имеют ссылки на родительский и все дочерние, корневые узлы имеют нуль как родительский), так что ни один узел не посещается до своих предков? Брауни указывает на нерекурсивность.Прогулка по дереву, родительский первый

+1

Связанный: http://stackoverflow.com/questions/754439/iterative-tree-walking –

ответ

6

Псевдо код:

NodesToVisit = some stack or some list 
NodesToVisit.Push(RootNode) 

While NodesToVisit.Length > 0 
{ 
    CurNode = NodesToVisit.Pop() 
    For each Child C in CurNode 
     NodesToVisit.Push(C) 
    Visit(CurNode) (i.e. do whatever needs to be done) 
} 

Edit: Рекурсивный или нет?
Для того, чтобы быть технически правильно, и как указал AndreyT и других в этом сообщении, этот подход форма рекурсивный алгоритм, посредством чего явно управляемого стек используется вместо стека процессора и где рекурсии происходит на уровне цикла While. При этом, она отличается от рекурсивной реализации по себе в пару тонких еще значимых способов:

  • только «переменные» выталкиваются в стек; в стеке нет «фрейма стека» и связанного с ним обратного адреса, единственный «обратный адрес» является неявным для цикла while, и есть только один его экземпляр.
  • «Стек» может использоваться в качестве списка, в котором следующий «кадр» можно было бы взять в любом месте списка, не нарушая при этом никакой логики.
+0

OK. Это не был академический вопрос. Это был практический вопрос. Это отвечает на это удовлетворительным образом, не заставляя меня думать или делать какие-либо дальнейшие чтения. Благодарю. (Вероятно, это заставит меня думать позже, когда я получаю время, но это прекрасно ... Полезно даже ...) Работа выполнена. Еще раз спасибо :) – George

0

Создайте список узлов в корне (уровень 0), поочередно перебирайте каждый узел и найдите прямых детей (родительский узел которых является узлом, с которого мы в настоящее время смотрим) (уровень 1), когда закончите с уровень 0 перемещается на итерирующий уровень 1 и т. д., пока у вас не останется оставшихся невидимых узлов.

1

Используйте набор узлов. Поместите корень в набор, чтобы начать. Затем в цикле вытащите узел из набора, зайдите в него, а затем поместите его детей в набор. Когда набор пуст, все готово.

+0

Вы хотите, чтобы структура данных была FIFO, а не только старым контейнером, чтобы гарантировать условие предзаказа. –

+0

В этом вопросе не было такого требования. –

1

В псевдокоде:

currentList = list(root) 
nextList = list() 
while currentList.count > 0: 
    foreach node in currentList: 
     nextList.add(node.children) 
    currentList = nextList 
3

Вы ищете обход. Я думаю, вы можете сделать это нерекурсивно с очереди :. В псевдокоде:

Создайте пустую очередь, затем нажмите корневой узел.

while nonempty(q) 
    node = pop(q) 
    visit(node) 
    foreach child(node) 
    push(q,child) 
+0

Это будет * нерекурсивная реализация * рекурсивного алгоритма *. Замена подразумеваемого стека явным стеком не превращает рекурсивный алгоритм в нерекурсивный. Фактически, он вообще не изменяет алгоритм. То, что у вас выше, очевидно, является рекурсивным (что касается алгоритма). – AnT

+5

Обычно, когда люди говорят, что они не хотят рекурсии, они имеют в виду рекурсию на уровне функций. Это удовлетворяет этому условию. –

+0

Ну, иногда да. Тем не менее, проблема, которую мы рассматриваем здесь, специально разработана для создания действительно нерекурсивного решения (т. Е. Нерекурсивного алгоритма). Мертвая распродажа - наличие родительских указателей. Ваше «нерекурсивное» решение не использует родительские указатели. Разве ты не удивляешься, почему они там? Они специально предназначены для того, чтобы обеспечить истинное нерекурсивное решение, которое требует O (1) памяти. – AnT

1

Если начать в корневом узле, и только посетить родитель/ребенок узлов вы уже побывали, не никакого способа для обхода дерева таким образом, что вы посещаете узел, прежде чем посетить его предок.

Будет работать любой вид обхода, глубина сначала (рекурсивная/стековая), ширина первой (на основе очереди), ограниченная глубиной или просто вытаскивание их из неупорядоченного набора.

«Лучший» метод зависит от дерева. Ширина сначала хорошо работала бы для очень высокого дерева с несколькими ветвями. Глубина сначала будет хорошо работать для деревьев со многими ветвями.

Поскольку узлы фактически имеют указатели на своих родителей, существует также алгоритм с постоянной памятью, но он намного медленнее.

+0

Te op сказал, что «* no * узел посещается перед его предками». Так что все наоборот. – AnT

+0

Что еще? Вы правильно прочитали мой ответ? – Artelius

+0

Возможно нет. Я думал, что в вашем первом предложении вы утверждали, что проблема не может быть решена, так как требование о посещении (которое, я предполагаю, вы неправильно поняли) невозможно удовлетворить. – AnT

3

Если у вас есть ссылки на всех детей и на родителя, то нерекурсивный алгоритм довольно тривиален. Просто забудьте, что у вас есть дерево. Подумайте, что это лабиринт, где каждая ссылка «родитель-ребенок» является обычным двунаправленным коридором от одного перехода к другому. Все, что вам нужно сделать, чтобы пройти весь лабиринт, - это превратиться в следующий коридор слева на каждом перекрестке. (В качестве альтернативы, подумайте об этом, ища лабиринт левой рукой, всегда касаясь стены с левой стороны). Если вы начинаете с корневого перехода (и двигаетесь в любом направлении), вы будете ходить по всему дереву, всегда посещая родителей перед детьми. Каждый «коридор» в этом случае будет перемещаться дважды (в одном направлении и в другом), и каждое «соединение» (узел) будет посещаться столько раз, сколько «коридоры» присоединяются к нему.

+0

Это постоянный алгоритм памяти, о котором я упоминал. – Artelius

1

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

Ссылка: http://en.wikipedia.org/wiki/Iterative_deepening

+0

+1 из-за вашего рассмотрения сложность пространства - но почему бы просто не использовать поиск глубины? – Artelius

+0

Многие деревья на практике имеют тенденцию быть глубже, чем «более широкие», особенно. в процессах принятия решений AI. В вопросе не указано, является ли дерево конечным, но вы можете столкнуться с циклом. Одна из причин итеративного углубления - это то, что она полна (найдет решение). – mduvall

1

Вот действительно нерекурсивен подход: нет стеки, постоянное пространство. Этот код Python предполагает, что каждый узел содержит список детей, а также о том, что объекты узла не определяют равенство, так что функция «индекс» сравнивает тождество:

def walkTree(root, visit_func): 
    cur = root 
    nextChildIndex = 0 

    while True: 
     visit_func(cur) 

     while nextChildIndex >= len(cur.children) and cur is not root: 
      nextChildIndex = cur.parent.children.index(cur) + 1 
      cur = cur.parent 

     if nextChildIndex >= len(cur.children): 
      break 

     cur = cur.children[nextChildIndex] 
     nextChildIndex = 0 

Я уверен, что это может быть отполировано до немного, сделано более кратким и легче читать, но это суть.

 Смежные вопросы

  • Нет связанных вопросов^_^