chandan374
Build binary tree from level order array
I am new to elixer, I need some help to construct binary tree from list of element(given in level order). Due to immutability i’m facing difficutly to implement this.
For ex: [1, 2, 3, -1, -1, -1, -1] will be represented as:
1
/ \
2 3
Here -1 means, node will be null.
Expected Output Format:
TreeNode{
data: 1,
left: TreeNode{
data: 2,
left: nil,
right: nil
},
right: TreeNode{
data: 3,
left: nil,
right: nil
}
}
In ruby, we can implement in this way.
def input_tree(input)
val = input[0]
input.shift()
root = TreeNode.new(val)
queue = []
queue.push(root)
while queue.size > 0
current_node = queue[0]
queue.shift()
leftValue = input[0]
input.shift()
rightValue = input[0]
input.shift()
if leftValue != -1
leftNode = TreeNode.new(leftValue)
current_node.left = leftNode
queue.push(leftNode)
end
if rightValue != -1
rightNode = TreeNode.new(rightValue)
current_node.right = rightNode
queue.push(rightNode)
end
end
return root
end
TIA ![]()
Marked As Solved
al2o3cr
Here’s an approach that uses a pair of recursive functions:
defmodule TreeNode do
defstruct ~w[data left right]a
end
defmodule TreeBuild do
@nothing -1
def run([root | rest]) do
[[left, right]] = subnodes(1, rest)
%TreeNode{data: root, left: left, right: right}
end
def subnodes(0, []), do: []
def subnodes(count, input) do
{roots, rest} = Enum.split(input, 2*count)
next_count = Enum.count(roots, & &1 != @nothing)
child_nodes = subnodes(next_count, rest)
roots
|> build_nodes(child_nodes, [])
|> Enum.chunk_every(2, 2, [nil])
end
def build_nodes([], _, acc), do: Enum.reverse(acc)
def build_nodes([@nothing | roots], child_nodes, acc) do
build_nodes(roots, child_nodes, [nil | acc])
end
def build_nodes([root | roots], [], acc) do
node = %TreeNode{data: root, left: nil, right: nil}
build_nodes(roots, [], [node | acc])
end
def build_nodes([root | roots], [[left, right] | child_nodes], acc) do
node = %TreeNode{data: root, left: left, right: right}
build_nodes(roots, child_nodes, [node | acc])
end
end
TreeBuild.run([1, 2, 3, -1, -1, -1, -1])
TreeBuild.run([5,4,8,11,-1,17,4,7,-1,-1,-1,5, -1, -1, -1, -1])
Some notes on the implementation:
- the sample input has one fewer -1 on the end than I expected at first; this version of
build_nodesis tolerant of any number of trailing -1s. -
chunk_everyis used to tidily handle cases wherebuild_nodesreturns an odd number of nodes by providing anilforleftovers
@Eiji I believe the error in your diagram is the two -1s under the -1 that’s a right-child of 4. -1s on a given level shouldn’t consume any input values in the next level.
Also Liked
Eiji
(…) if a node has an index
i, its children are found at indices2i+1(for the left child) and2i+2(for the right) (…)
With above writing code is really simple:
defmodule TreeNode do
defstruct ~w[data left right]a
end
defmodule Example do
def sample(input, index \\ 0) do
data = Enum.at(input, index)
unless data in [nil, -1] do
%TreeNode{
data: data,
left: sample(input, index * 2 + 1),
right: sample(input, index * 2 + 2)
}
end
end
end
iex> Example.sample([1, 2, 3, -1, -1, -1, -1])
%TreeNode{
data: 1,
left: %TreeNode{data: 2, left: nil, right: nil},
right: %TreeNode{data: 3, left: nil, right: nil}
}
cevado
have you tried using gb_trees from erlang?
https://www.erlang.org/doc/man/gb_trees.html
adamu
benwilson512
Hi @chandan374 can you show what you’ve tried so far?
Eiji
The tree representation you provided is incorrect as 5 leaf should be a left child of 17 node. Therefore I think my example is working.
This is how it should look:
5
/ \
4 8
/ \ / \
11 -1 17 4
/ \ / \ / \ / \
7 -1 -1 -1 5 -1 -1 -1
/
-1
# after removing -1 entries
5
/ \
4 8
/ / \
11 17 4
/ /
7 5
To prove that I have also wrote another solution. This time my code is building the tree in reverse mode (I’m starting with leafs) based on pattern matching.
defmodule TreeNode do
defstruct ~w[data left right]a
end
defmodule Example do
def sample(input) do
input |> split() |> transform(nil)
end
# we split a list by count
# on top is only root, so default count is always 1
# every other level have count * 2 nodes
defp split(list, acc \\ [], count \\ 1) do
{left, right} = Enum.split(list, count)
# we call split until right side is empty
if right == [] do
[left | acc]
else
# after each split the left side is added to acc
# therefore we would have leafs list as fist item in acc
# and a single root data as last item of acc
split(right, [left | acc], count * 2)
end
end
defp transform([leafs | groups], nil) do
# transform leafs into initial acc
# which would be used in transform groups
transform(groups, {Enum.map(leafs, &transform_leaf/1), []})
end
# finishing by returning root node
defp transform([], {[root_node], []}), do: root_node
defp transform([[] | groups], {[], acc_right}) do
# reached end of group
# all nodes we have collected in acc_right
# are moved to acc_left
# so they would be used in next group
transform(groups, {Enum.reverse(acc_right), []})
end
defp transform([[-1 | group] | groups], {[], acc_right}) do
# skipping -1 with no fake leafs
transform([group | groups], {[], [nil | acc_right]})
end
defp transform([[-1 | group] | groups], {[nil, nil | acc_left], acc_right}) do
# skipping -1 with transformed fake leafs
transform([group | groups], {acc_left, [nil | acc_right]})
end
defp transform([[data | group] | groups], {[], acc_right}) do
# node #{data} does not have children
transform([group | groups], {[], [transform_node(data) | acc_right]})
end
defp transform([[data | group] | groups], {[left], acc_right}) do
# node #{data} have only left child
transform([group | groups], {[], [transform_node(data, left) | acc_right]})
end
defp transform([[data | group] | groups], {[left, right | acc_left], acc_right}) do
# node #{data} have both children
transform([group | groups], {acc_left, [transform_node(data, left, right) | acc_right]})
end
defp transform_leaf(-1), do: nil
defp transform_leaf(data), do: %TreeNode{data: data}
defp transform_node(data, left \\ nil, right \\ nil) do
# transforming node
%TreeNode{data: data, left: left, right: right}
end
end
iex> Example.sample([5, 4, 8, 11, -1, 17, 4, 7, -1, -1, -1, 5, -1, -1, -1, -1])
%TreeNode{
data: 5,
left: %TreeNode{
data: 4,
left: %TreeNode{
data: 11,
left: %TreeNode{data: 7, left: nil, right: nil},
right: nil
},
right: nil
},
right: %TreeNode{
data: 8,
left: %TreeNode{
data: 17,
left: %TreeNode{data: 5, left: nil, right: nil},
right: nil
},
right: %TreeNode{data: 4, left: nil, right: nil}
}
}
Both examples returns exactly same data. Feel free to ask if you have any questions. ![]()







