Пол десятилетия назад я сидел в классе структур данных, где профессор предлагал дополнительные кредиты, если кто-то мог пройти по дереву без использования рекурсии, стека, очереди и т. Д. (Или любых других подобных структур данных) и всего лишь нескольких указателей. Я придумал, как мне казалось, очевидный ответ на этот вопрос, который в конечном итоге был принят профессором. Я сидел в отдельном классе по математике с другим профессором в том же отделе - и он утверждал, что невозможно пройти по дереву без рекурсии, стека, очереди и т. Д., И что мое решение было неверным.
Так это возможно или невозможно? Почему или почему нет?
Редактировать: чтобы добавить некоторые пояснения, я реализовал это в двоичном дереве, которое имело три элемента - данные, хранящиеся в каждом узле и указатели на двух дочерних элементов. Мое решение может быть распространено на n-арные деревья с несколькими изменениями.
Мой учитель по структурам данных не накладывал никаких ограничений на изменение дерева, и позже я узнал, что его собственное решение состояло в том, чтобы использовать дочерние указатели, чтобы указывать назад дерево по пути вниз. Мой дискретный профессор математики сказал, что любая мутация дерева означает, что оно больше не является деревом в соответствии с математическим определением дерева, его определение также исключает любые указатели на родителей - что будет соответствовать случаю, когда я решил это выше.
источник
Ответы:
Много исследований в этой области было проведено с использованием метода «дешевого» обхода деревьев и общих структур списков в контексте сбора мусора.
Бинарное дерево с резьбой - это адаптированное представление бинарных деревьев, в которых некоторые ниль-указатели используются для связи с последующими узлами в дереве. Эта дополнительная информация может быть использована для обхода дерева без стека. Тем не менее, дополнительный бит на узел необходим, чтобы отличить потоки от дочерних указателей. Википедия: Tree_traversal
Насколько я знаю, двоичные деревья, реализованные с использованием указателей обычным способом (указатель слева и справа на узел), могут быть пройдены с использованием метода потоков в методе, приписанном Моррису . NIL-указатели временно повторно используются для наведения пути к корню. Умная часть заключается в том, что во время обхода можно отличить исходные ребра от временных нитей-связей, используя способ, которым они образуют циклы в дереве).
Хорошая часть: нет дополнительной структуры данных. Плохая часть: немного жульничает, стек хитроумно лежит внутри дерева. Очень умно.
Доказательство скрытого стека показано в P. Mateti и R. Manghirmalani: пересмотрен алгоритм обхода дерева Морриса DOI: 10.1016 / 0167-6423 (88) 90063-9
Дж. М. Моррис: Обход бинарных деревьев просто и дешево. IPL 9 (1979) 197-200 DOI: 10.1016 / 0020-0190 (79) 90068-1
Тогда есть также сканирование Линдстрема . Этот метод «вращает» три указателя, участвующих в каждом узле (родитель и два потомка). Если вы хотите выполнить какой-либо приличный алгоритм предварительного или пост-заказа, вам нужны дополнительные биты на узел. Если вы просто хотите посетить все узлы (три раза, но никогда не знаете, какое посещение вы выполняете), то это можно сделать без битов.
Г. Линдстрем: Сканирование структур списка без стеков или битов тегов. IPL 2 (1973) 47-51. DOI: 10.1016 / 0020-0190 (73) 90012-4
Возможно, самый простой способ - это метод Робсона . Здесь стек, необходимый для классического алгоритма, проходит через листья.
JM Robson: улучшенный алгоритм обхода двоичных деревьев без вспомогательного стека IPL 1 (1973) 149-152. 10,1016 / 0020-0190 (73) 90018-5
IPL = письма для обработки информации
источник
источник
Мое решение состояло в том, чтобы использовать обход вначале, используя вложенные циклы for для грубой силы дерева. Это никоим образом не эффективно, и действительно рекурсивная структура данных, такая как дерево, требует рекурсивного обхода, но вопрос не в том, можно ли эффективно обходить дерево, а в том, возможно ли это вообще.
Для первых нескольких уровней это будет выглядеть так, как вы можете видеть, побитовый оператор в псевдокоде просто решает повернуть влево или вправо двоичное дерево:
Для n-ар вы бы взяли i% (maxChildren ** j) / j, чтобы определить, какой путь выбрать между 0 и maxChildren.
В каждом узле n-ary вам также необходимо проверить, больше ли число дочерних элементов, чем maxChildren, и обновить его соответствующим образом.
источник
depth
ширина превышаетint
?i
depth
depth
i