diff --git a/lib/mb_binary_tree/traverse.ex b/lib/mb_binary_tree/traverse.ex index 8dedf97..872d4ac 100644 --- a/lib/mb_binary_tree/traverse.ex +++ b/lib/mb_binary_tree/traverse.ex @@ -51,4 +51,62 @@ defmodule MbBinaryTree.Traverse do end defp postorder_acc([]), do: nil + + @doc """ + Perform a breadth-first traversal of a binary tree. + + ## Examples + + iex> MbBinaryTree.Traverse.breadth_first([1, [2, nil, nil], [3, nil, nil]]) |> Enum.to_list() + [1, 2, 3] + iex> MbBinaryTree.Traverse.breadth_first([12, [4, MbBinaryTree.new(2), MbBinaryTree.new(5)], [26, MbBinaryTree.new(14), [28, MbBinaryTree.new(27), MbBinaryTree.new(30)]]]) |> Enum.to_list() + [12, 4, 26, 2, 5, 14, 28, 27, 30] + """ + def breadth_first(tree) do + queue = :queue.from_list([tree]) + Stream.unfold(queue, &breadth_first_acc/1) + end + + defp breadth_first_acc({[], []}), do: nil + + defp breadth_first_acc(queue) do + {{:value, tree}, queue} = :queue.out(queue) + + case tree do + [value | [nil, nil]] -> {value, queue} + [value | [left, nil]] -> {value, queue |> q_in(left)} + [value | [nil, right]] -> {value, queue |> q_in(right)} + [value | [left, right]] -> {value, queue |> q_in(left) |> q_in(right)} + nil -> breadth_first_acc(queue) + end + end + + # A convenience function to allow piping above by swapping the argument order. + defp q_in(queue, item), do: :queue.in(item, queue) + + @doc """ + Perform an inorder traversal of a binary tree. + + ## Examples + + iex> MbBinaryTree.Traverse.inorder([1, [2, nil, nil], [3, nil, nil]]) |> Enum.to_list() + [2, 1, 3] + iex> MbBinaryTree.Traverse.inorder([12, [4, MbBinaryTree.new(2), MbBinaryTree.new(5)], [26, MbBinaryTree.new(14), [28, MbBinaryTree.new(27), MbBinaryTree.new(30)]]]) |> Enum.to_list() + [2, 4, 5, 12, 14, 26, 27, 28, 30] + """ + def inorder(tree) do + Stream.unfold([tree], &inorder_acc/1) + end + + defp inorder_acc([tree | stack]) do + case tree do + [value | [nil, nil]] -> {value, stack} + [value | [left, nil]] -> inorder_acc([left, [value | [nil, nil]] | stack]) + [value | [nil, right]] -> {value, [right | stack]} + [value | [left, right]] -> inorder_acc([left, [value | [nil, nil]], right | stack]) + nil -> inorder_acc(stack) + end + end + + defp inorder_acc([]), do: nil end