elixir-binary-tree/lib/mb_binary_tree.ex
2025-03-14 23:09:26 -07:00

124 lines
3.5 KiB
Elixir

defmodule MbBinaryTree do
@moduledoc """
Documentation for `MbBinaryTree`.
"""
@type t :: nil | nonempty_maybe_improper_list()
@spec new(any) :: t
@doc """
Create a new tree with a root node.
## Examples
iex> MbBinaryTree.new(4)
[4, nil, nil]
"""
def new(root) do
[root, nil, nil]
end
@spec is_tree?(any) :: boolean
@doc """
Test whether argument is a valid binary tree.
## Examples
iex> MbBinaryTree.new(19) |> MbBinaryTree.is_tree?()
true
iex> MbBinaryTree.is_tree?([12])
false
iex> MbBinaryTree.is_tree?(12)
false
iex> MbBinaryTree.is_tree?([12, [4, MbBinaryTree.new(2), MbBinaryTree.new(5)], [26, MbBinaryTree.new(14), [28, MbBinaryTree.new(27), MbBinaryTree.new(30)]]])
"""
def is_tree?(nil), do: true
def is_tree?([_, nil, nil]), do: true
def is_tree?([_, nil, right]), do: is_tree?(right)
def is_tree?([_, left, nil]), do: is_tree?(left)
def is_tree?([_, left, right]), do: is_tree?(left) && is_tree?(right)
def is_tree?(_), do: false
@spec left(t) :: t
@doc """
Get the left child of a tree.
## Examples
iex> MbBinaryTree.left([1, MbBinaryTree.new(2), MbBinaryTree.new(3)])
[2, nil, nil]
iex> MbBinaryTree.new(12) |> MbBinaryTree.left()
nil
iex> MbBinaryTree.left([12, [4, MbBinaryTree.new(2), MbBinaryTree.new(5)], [26, MbBinaryTree.new(14), [28, MbBinaryTree.new(27), MbBinaryTree.new(30)]]])
[4, [2, nil, nil], [5, nil, nil]]
"""
def left([_, left | _]), do: left
def left(_), do: nil
@spec right(t) :: t
@doc """
Get the right child of a tree.
## Examples
iex> MbBinaryTree.right([1, MbBinaryTree.new(2), MbBinaryTree.new(3)])
[3, nil, nil]
iex> MbBinaryTree.new(12) |> MbBinaryTree.right()
nil
iex> MbBinaryTree.right([12, [4, MbBinaryTree.new(2), MbBinaryTree.new(5)], [26, MbBinaryTree.new(14), [28, MbBinaryTree.new(27), MbBinaryTree.new(30)]]])
[26, [14, nil, nil], [28, [27, nil, nil], [30, nil, nil]]]
"""
def right([_, _, right]), do: right
def right(_), do: nil
@spec update_left(t, t) :: t
@doc """
Update the left child of a tree.
## Examples
iex> MbBinaryTree.update_left([1, MbBinaryTree.new(2), MbBinaryTree.new(3)], MbBinaryTree.new(4))
[1, [4, nil, nil], [3, nil, nil]]
iex> MbBinaryTree.new(12) |> MbBinaryTree.update_left(MbBinaryTree.new(4))
[12, [4, nil, nil], nil]
"""
def update_left([node, _left, right], new_left), do: [node, new_left, right]
@spec update_right(t, t) :: t
@doc """
Update the right child of a tree.
## Examples
iex> MbBinaryTree.update_right([1, MbBinaryTree.new(2), MbBinaryTree.new(3)], MbBinaryTree.new(4))
[1, [2, nil, nil], [4, nil, nil]]
iex> MbBinaryTree.new(12) |> MbBinaryTree.update_right(MbBinaryTree.new(4))
[12, nil, [4, nil, nil]]
"""
def update_right([node, left, _right], new_right), do: [node, left, new_right]
@doc """
Calculate the depth of a binary tree.
## Examples
iex> MbBinaryTree.depth(nil)
0
iex> MbBinaryTree.depth([1, nil, nil])
1
iex> MbBinaryTree.depth([1, [2, nil, nil], [3, nil, nil]])
2
iex> MbBinaryTree.depth([1, [2, nil, nil], [3, [4, nil, nil], [5, nil, nil]]])
3
iex> MbBinaryTree.depth([12, [4, MbBinaryTree.new(2), MbBinaryTree.new(5)], [26, MbBinaryTree.new(14), [28, MbBinaryTree.new(27), MbBinaryTree.new(30)]]])
4
"""
def depth(tree) do
case tree do
nil -> 0
[_, nil, nil] -> 1
[_, left, right] -> 1 + max(depth(left), depth(right))
end
end
end