Как правило, вы можете просто вызвать &Enum.reverse/1
в результирующем списке.Многие алгоритмы, которые вы найдете, делают это под капотом из-за характера построения списков в erlang.Я думаю, что даже &Enum.map/2
использует его.
Кроме этого, есть гораздо более простой способ написать свою функциональность с помощью функциональных головок.Я полагаю, что вы ищете обход дерева по порядку, в котором каждый посещенный узел добавляется в список, но вы можете легко изменить его, включив также обход по порядку и порядку.Вот модуль с функциями отображения / сокращения, которые вы ищете.
defmodule Tree do
# This just uses the reduce functions defined below to create a map.
def map_inorder(root) do
root
|> reduce_inorder([], fn val, acc ->
[val | acc]
end)
|> Enum.reverse()
end
# This is the core functionality of the function for an inorder traversal
# It processes the left subtree then calls the reducer on the root node
# and then processes the right subtree.
def reduce_inorder({:node, val, left, right}, acc, fun) do
left_subtree = reduce_inorder(left, acc, fun)
visit_root = fun.(val, left_subtree)
reduce_inorder(right, visit_root, fun)
end
# Nil means that you've reached a leaf. There's nothing else to process
def reduce_inorder(nil, acc, _fun) do
acc
end
# Node does not match the spec you have for the record. Return an error
def reduce_inorder(_other, _, _) do
:malformed_node
end
end
Алгоритмы обхода двоичного дерева очень просты для понимания.Вот пост, который хорошо их объясняет.
https://www.geeksforgeeks.org/tree-traversals-inorder-preorder-and-postorder/
Ура!
РЕДАКТИРОВАТЬ
Я понял, что вы говоритео ширине первого поиска (BFS), который является совершенно другим алгоритмом.По сути, вы должны помещать узлы в очередь, а не в стек, как это делают алгоритмы обхода preorder / postorder / inorder.
BFS обеспечивает обработку всех узлов в порядке слева направо на одной и той же глубинедерево.Как правило, вы начинаете с корневого узла как единственного узла в очереди.Вы обрабатываете этот узел и затем помещаете его левого и правого потомков в очередь в указанном порядке, а затем повторяете новую очередь.К счастью, я вспомнил, что в erlang есть модуль :queue
, который делает это намного проще.Вы можете найти код ниже:
defmodule Tree do
def map_tree(root) do
root
|> reduce_tree([], fn val, acc ->
[val | acc]
end)
|> Enum.reverse()
end
def reduce_tree(root, acc, reducer) do
:queue.new()
|> queue_in(root)
|> process_queue(acc, reducer)
end
def process_queue(queue, acc, reducer) do
case queue_out(queue) do
{{:value, {:node, val, left, right}}, popped} ->
# Process the head of the queue which is the next item in the traversal
new_acc = reducer.(val, acc)
# Push in the left then right so that they are processed in that order
# and so that they are processsed behind earlier nodes that have been
# found
popped
|> queue_in(left)
|> queue_in(right)
|> process_queue(new_acc, reducer)
_other ->
# Your queue is empty. Return the reduction
acc
end
end
# These are convenience methods. If the value being pushed in is nil then
# ignore it so that it is not processed
def queue_in(queue, nil) do
queue
end
def queue_in(queue, val) do
:queue.in(val, queue)
end
def queue_out(queue) do
:queue.out(queue)
end
end
Самое замечательное в этом подходе состоит в том, что он имеет хвостовую рекурсию.
Надеюсь, это поможет.Вот отличная статья о BFS:
https://medium.com/basecs/breaking-down-breadth-first-search-cebe696709d9