This is the third part of a series about data structures in JavaScript. Here is the index with the publications:

Like in the previous, I created an interactive SPA with React to understand Binary Trees behavior.

How a Binary Tree works?

A Binary Tree is formed by Binary nodes that in this case have a Pair {Key, Value}, a link to the left node and a link to the right node.

The first node is named root and like every node, has 2 childrens. The keys of nodes in the left children of the root always will be less than root’s key. The keys of the left children always will be higher than root’s key. In the above image you can check that.

That conditions are applied recursively in every insertion until finding a node with a free link that accomplishes them.

A leaf is a node that doesn’t have childs. Here is the Node class:

Very simple. The BinaryTree data is even simpler:

class BinaryTreeNode {

constructor() {

this.root = null;

}

…

}

The only reference is the root. It acts as an entry point for every method.

Well, let’s see how the insertion works:

In a first instance, you call the insert function with a key,value pair. It does some basic checks and then calls the insertNode function. It receives the node where it needs to iterate from and the node to insert. Once the function is called it does a triple condition comparing keys:

New Node is lower than entry point. If left child is free, then newNode is assigned, else, go to check the left node recursively.

New Node is higher than entry point. Same workflow but in the right node.

New Node key is equal than entry point key. In this case a node also had the selected key and the value is overwritten.

This second method is separated because is called resursively and doesn’t need to check the pre-requisites in every iteration. Let’s go with the remove method:

The remove method checks a pre-requisite, and calls the removeNode using root as entry point and receiver of the returned value from that method.

RemoveNode works recursively like insertion and has a similar worklflow. The main difference comes when the node is found. If it is a leaf, the work is done, in other case we have to rebuild the child of the node we are going to remove.

The cyclomatic complexity of this method is 2n because we have to find and rebuild. Now let’s check the find method:

Like insert method, find has a complexity of O(log2 n) as you don’t need to run over every tree’s element. The algorithm is easy to understand. It goes recursively though childs until find the wanted node.

Binary Trees have 4 different paths:

Inorder : Receiving an entry point and a function to execute, it goes recursively for the left node, then root (executing function over it) and finally going through right node. With the curren behavior and ordenation of this trees. Inorder would do a sorted path, because in the left tree there will be the smaller keys.

: Receiving an entry point and a function to execute, it goes recursively for the left node, then root (executing function over it) and finally going through right node. With the curren behavior and ordenation of this trees. Inorder would do a sorted path, because in the left tree there will be the smaller keys. Preorder : Same behavior but executing first the function over the node and then recursively left and right node.

: Same behavior but executing first the function over the node and then recursively left and right node. Postorder: Left and right nodes are run first recursively while root is the last one.

Level order: Easy to understand. From left to right. Once it runs over the whole level, then goes with the next, and iterating until the last level. That’s the theory, in the practice I have an array for every level with a length of 2^(level-1). In the level every node has an index to reference its childs and know where to store them in the level array.

A level array will have n elements filled, and length-n elements void. They have at least 1 element filled.

Let’s explain them with an example:

Inorder: 1 3 4 6 7 8 10 13 14

Preorder: 8 3 1 6 4 7 10 14 13

Postorder: 1 4 7 6 3 13 14 10 8

Level Order: [ [8], [3, 10], [1, 6, ‘’, 14], [‘’, ‘’, 4, 7, ‘’, ‘’, 13, ‘’] ];

4 levels <-> 4 arrays

In the level n the levelArray has a 2^(n-1) length. In level 4: 2³=8, so 8 items.

When and why should you use Binary Trees?

With a <key,value> pair in every node we can store everything in the value while using the key for methods purposes. The complexity for insert or find in the worst case is O(log2 n) because you have not to go through every element to find the one you want. In the above example if it was a sorted array and you wanted to find the element 13 you should run through 6 elements before find 13. In the BTree you just need to go throug root, 10 and 14. The sorting method of binary trees is the key for save a lot of time searching. Insert function has the same behavior and cost while remove has a bit more cost, O(n) because you have to rebuild the childs of removed node. But in an array you have also to deal with an O(n) cost in remove.

The lone restriction here is the exclusivity of keys. There can not be repeated items. However, you can store whatever you want in the value, so depending of your needs you could store something like an array of values and have several insertions. In that case you should modify that method.

Where can I check the code and the application?

The application is stored in Heroku:

The code is in Github. Feel free to send me Pull Requests of changes, improvements or whatever you think could be interesting. I am open to collaborations.

You can reach me on twitter (@Oliver_ap)