Binary 将二叉树展平到列表(有序)

Binary 将二叉树展平到列表(有序),binary,Binary,我正在尝试实现一种算法,该算法将树作为输入,并返回一个列表,其中所有值的顺序都正确(从上到下,每行从左到右),但我遇到了问题。无序操作的简单方法是减少整个列表,其中每个节点都附加到累积列表中 这是我编写的代码,用于简化树(用elixir编写),其中每个节点都有一个左右分支,可以是另一个节点,也可以是nil: def reduce(nil, op, init), do: init def reduce({:node, n, left, right}, op, init) do

我正在尝试实现一种算法,该算法将树作为输入,并返回一个列表,其中所有值的顺序都正确(从上到下,每行从左到右),但我遇到了问题。无序操作的简单方法是减少整个列表,其中每个节点都附加到累积列表中

这是我编写的代码,用于简化树(用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 def reduce(nil,op,init),do:init def reduce({:node,n,left,right},op,init)do reduce(右,op,reduce(左,op,op.(n,init))) 结束 这样调用以获取树(但顺序错误):

结果=减少(树,fn(节点,acc)->[acc |节点]结束,[])
有什么提示吗?

一般来说,您可以在结果列表中调用
&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
二叉树遍历算法非常容易理解。这里有一篇文章很好地解释了它们

干杯

编辑

我意识到你说的是广度优先搜索(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的好文章:


根据一般经验,您可以在结果列表中调用
&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
二叉树遍历算法非常容易理解。这里有一篇文章很好地解释了它们

干杯

编辑

我意识到你说的是广度优先搜索(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的好文章: