我正在尝试实现一种算法,该算法将树作为输入并以正确的顺序(从上到下,每行从左到右)返回具有所有值的列表,但是我遇到了麻烦。进行无序操作的简单方法是减少将每个节点附加到累积列表的整个列表。
这是我编写的用于减少一棵树的代码(用elixir编写),其中每个节点都有左右分支,可以是另一个节点或nil:
def reduce(nil, op, init), do: init def reduce({:node, n, left, right}, op, init) do reduce(right, op, reduce(left, op, op.(n, init))) end
像这样调用以获取树(但顺序错误):
result = reduce(tree, fn (node, acc) -> [acc|node] end, [])
有什么提示吗?
答案 0 :(得分:0)
作为一般经验法则,您可以仅在结果列表中调用&Enum.reverse/1
。由于使用erlang构造列表的方式的性质,您会发现很多算法都是在后台进行的。我认为甚至&Enum.map/2
都使用它。
此外,还有一种使用函数头编写功能的简便方法。我相信您正在寻找要顺序遍历的树,其中将每个访问的节点都添加到列表中,但是您可以轻松地对其进行修改,以包括后序遍历和预序遍历。这是一个包含您要查找的map / reduce函数的模块。
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),这是一种完全不同的算法。基本上,您必须将节点推入队列而不是堆栈,这是前置/后置/有序遍历算法的作用。
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