Каков наилучший способ посетить все узлы связанного дерева (все узлы имеют ссылки на родительский и все дочерние, корневые узлы имеют нуль как родительский), так что ни один узел не посещается до своих предков? Брауни указывает на нерекурсивность.Прогулка по дереву, родительский первый
ответ
Псевдо код:
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, и есть только один его экземпляр.
- «Стек» может использоваться в качестве списка, в котором следующий «кадр» можно было бы взять в любом месте списка, не нарушая при этом никакой логики.
OK. Это не был академический вопрос. Это был практический вопрос. Это отвечает на это удовлетворительным образом, не заставляя меня думать или делать какие-либо дальнейшие чтения. Благодарю. (Вероятно, это заставит меня думать позже, когда я получаю время, но это прекрасно ... Полезно даже ...) Работа выполнена. Еще раз спасибо :) – George
Создайте список узлов в корне (уровень 0), поочередно перебирайте каждый узел и найдите прямых детей (родительский узел которых является узлом, с которого мы в настоящее время смотрим) (уровень 1), когда закончите с уровень 0 перемещается на итерирующий уровень 1 и т. д., пока у вас не останется оставшихся невидимых узлов.
Используйте набор узлов. Поместите корень в набор, чтобы начать. Затем в цикле вытащите узел из набора, зайдите в него, а затем поместите его детей в набор. Когда набор пуст, все готово.
Вы хотите, чтобы структура данных была FIFO, а не только старым контейнером, чтобы гарантировать условие предзаказа. –
В этом вопросе не было такого требования. –
В псевдокоде:
currentList = list(root)
nextList = list()
while currentList.count > 0:
foreach node in currentList:
nextList.add(node.children)
currentList = nextList
Вы ищете обход. Я думаю, вы можете сделать это нерекурсивно с очереди :. В псевдокоде:
Создайте пустую очередь, затем нажмите корневой узел.
while nonempty(q)
node = pop(q)
visit(node)
foreach child(node)
push(q,child)
Это будет * нерекурсивная реализация * рекурсивного алгоритма *. Замена подразумеваемого стека явным стеком не превращает рекурсивный алгоритм в нерекурсивный. Фактически, он вообще не изменяет алгоритм. То, что у вас выше, очевидно, является рекурсивным (что касается алгоритма). – AnT
Обычно, когда люди говорят, что они не хотят рекурсии, они имеют в виду рекурсию на уровне функций. Это удовлетворяет этому условию. –
Ну, иногда да. Тем не менее, проблема, которую мы рассматриваем здесь, специально разработана для создания действительно нерекурсивного решения (т. Е. Нерекурсивного алгоритма). Мертвая распродажа - наличие родительских указателей. Ваше «нерекурсивное» решение не использует родительские указатели. Разве ты не удивляешься, почему они там? Они специально предназначены для того, чтобы обеспечить истинное нерекурсивное решение, которое требует O (1) памяти. – AnT
Если начать в корневом узле, и только посетить родитель/ребенок узлов вы уже побывали, не никакого способа для обхода дерева таким образом, что вы посещаете узел, прежде чем посетить его предок.
Будет работать любой вид обхода, глубина сначала (рекурсивная/стековая), ширина первой (на основе очереди), ограниченная глубиной или просто вытаскивание их из неупорядоченного набора.
«Лучший» метод зависит от дерева. Ширина сначала хорошо работала бы для очень высокого дерева с несколькими ветвями. Глубина сначала будет хорошо работать для деревьев со многими ветвями.
Поскольку узлы фактически имеют указатели на своих родителей, существует также алгоритм с постоянной памятью, но он намного медленнее.
Te op сказал, что «* no * узел посещается перед его предками». Так что все наоборот. – AnT
Что еще? Вы правильно прочитали мой ответ? – Artelius
Возможно нет. Я думал, что в вашем первом предложении вы утверждали, что проблема не может быть решена, так как требование о посещении (которое, я предполагаю, вы неправильно поняли) невозможно удовлетворить. – AnT
Если у вас есть ссылки на всех детей и на родителя, то нерекурсивный алгоритм довольно тривиален. Просто забудьте, что у вас есть дерево. Подумайте, что это лабиринт, где каждая ссылка «родитель-ребенок» является обычным двунаправленным коридором от одного перехода к другому. Все, что вам нужно сделать, чтобы пройти весь лабиринт, - это превратиться в следующий коридор слева на каждом перекрестке. (В качестве альтернативы, подумайте об этом, ища лабиринт левой рукой, всегда касаясь стены с левой стороны). Если вы начинаете с корневого перехода (и двигаетесь в любом направлении), вы будете ходить по всему дереву, всегда посещая родителей перед детьми. Каждый «коридор» в этом случае будет перемещаться дважды (в одном направлении и в другом), и каждое «соединение» (узел) будет посещаться столько раз, сколько «коридоры» присоединяются к нему.
Это постоянный алгоритм памяти, о котором я упоминал. – Artelius
Я бы не согласился с первым поиском по ширине, поскольку сложность пространства часто является проклятием этого конкретного алгоритма поиска. Возможно, использование алгоритма итерационного углубления является лучшей альтернативой для такого типа использования, и он охватывает такой же тип обхода, как и поиск по ширине. Есть незначительные различия в работе с бахромой из поиска по ширине, но это не должно быть слишком сложно для (псевдо) кода.
+1 из-за вашего рассмотрения сложность пространства - но почему бы просто не использовать поиск глубины? – Artelius
Многие деревья на практике имеют тенденцию быть глубже, чем «более широкие», особенно. в процессах принятия решений AI. В вопросе не указано, является ли дерево конечным, но вы можете столкнуться с циклом. Одна из причин итеративного углубления - это то, что она полна (найдет решение). – mduvall
Вот действительно нерекурсивен подход: нет стеки, постоянное пространство. Этот код 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
Я уверен, что это может быть отполировано до немного, сделано более кратким и легче читать, но это суть.
Связанный: http://stackoverflow.com/questions/754439/iterative-tree-walking –