Tree Traversal with JavaScript
Tree Traversal with JavaScript

Part 6 of the Data Structures series with JavaScript

Photo by Dmitry Schemelev on Unsplash

In this article, we will be introduced to tree traversal. If you missed the last part of the series where we learned about trees, binary trees, and binary search trees, be sure to check out the article below first.

What is Tree Traversal?

Tree traversal is visiting every node once in a tree. Compared to a list, array, stack, or queue where there is only one linear path to visit all nodes, a tree does not have a specific order in which you can accomplish this. There are two approaches when it comes to tree traversal:

Depth-First Search — Exploring as far as possible down a branch before backtracking. Within depth-first search, there are three main orders; preOrder, postOrder, and inOrder.

Breadth-First Search — Explore all neighbor nodes at the current depth, before moving on to the next depth level.

The image below is a quick summary of what we will go through.

We will explore these four ways to traverse our tree. For this article, we will be using a binary tree as an example. The code below is our binary tree which we created in the last article. To quickly recap, we have a Node class for when we insert new nodes into our tree, and a Binary Tree class with two methods, insert and find.

Binary Search Tree

Depth-First Search

Depth-First Search means traversing a tree vertically before visiting any sibling nodes. We will travel down in our tree (depth-first) before traveling sideways (breadth-first). Please note we will using recursion in our solution. If you are not familiar with recursion, you can read more about it here.

In depth-first search, there are three steps we need to take. The order in which we do these steps will determine the output and order in which we visit the nodes.

Visit the node

Explore the left side

Explore the right side

PreOrder

We will first take a look at depth-first search preOrder. We will start at the root, and will first visit the node (this means we will add it to our data list, and we will not visit it again). Then we will traverse the left side of the tree, then the right side (we will do this for each node). The image below represents the order in which we will visit each node. We will start at 1 and add it to the list. Then check the left side of 1, which moves us to 2, and add 2 to the list. Then, check the left side of 2, which moves us to 3, then add 3 to the list. Since there is no left of 3, we will go back to 2, then check the right side of 2, which moves us to 4, then add 4 to the list. Then we will move back up the tree to 1, and check the right side of 1, which moves us to 5, then add 5 to the list.

Now let’s write out our function. We will first create a new method on our tree class and call it DFSPreOrder. We will need to create an array in our method to store our visited nodes, which we will call data.

We will then use recursion and write a helper function, called traverse. This function will accept a node as an argument. Remember, there are three steps we need to take (visit the node, explore the left side, explore the right side). Let’s first push the value of the node to our data array (visit the node). Then, we will check if the node has a left property. If it does, we will call the helper function again with the left node. Then, we will check if the node has a right property. If so, we will call the helper function with the right node.

Finally, we will invoke the helper function with the root node, and at the end return the data array.

DFSPreOrder() {

let data = [];

function traverse(node) {

data.push(node.value);

if(node.left) traverse(node.left);

if(node.right) traverse(node.right);

}

traverse(this.root);

return data;

}

PostOrder

In postOrder, we will be visiting the node last. What this means is that we will visit all child nodes first before visiting the parent node.

The image below represents the order in which we will visit each node. We start at the root again but notice the root is the last node that we visit. We first traverse the left side of the tree, which brings us to 3, then again to 1. Since there is no left or right of 1, we will visit 1. Then instead of visiting 3, we will traverse the right side of 3, which brings us to 2. Since there is no left or right of 2, we visit 2, then move back to 3, and visit 3. After visiting 3, we move back up to 5, but before visiting 5, we traverse the right side of 5, which moves us to 4. Since 4 has no left or right, we visit 4, then finally move back to 5, and visit 5.

The code for postOrder is very similar to our code for preOrder, except for one minor change in the steps we take in the helper function. In the helper function, instead of pushing the node value at the beginning, we will push the data after traversing both the left and right sides. That’s it!

DFSPostOrder() {

let data = [];

function traverse(node) {

if(node.left) traverse(node.left);

if(node.right) traverse(node.right);

data.push(node.value);

}

traverse(this.root);

return data;

}

InOrder

The last depth-first search order we will look at is inOrder. If we are using a binary search tree, this will give us an array with our values in ascending order, as the name suggests. The image below represents the order in which we will visit each node. Starting at the root, we will traverse the left side of the tree, moving us to 2, then to 1. Since 1 has no left or right, we will visit 1, then move back up to 2. Then we will visit 2. Then traverse the right side of 2, to move us to 3, and visit 3. Next, we will move back up the tree to 4, and visit 4. Finally, we will traverse the right side of 4, which moves us to 5, and we will visit 5.

Again, the code for inOrder is similar to preOrder and postOrder except for one change in the helper function. We first traverse the left side, then visit the node by pushing the value into our data array, then traverse the right side.

DFSInOrder() {

let data = [];

function traverse(node) {

if(node.left) traverse(node.left);

data.push(node.value);

if(node.right) traverse(node.right);

}

traverse(this.root);

return data;

}

Breadth-First Search

Let’s now move into breadth-first search. Instead of moving down a tree as we have done in depth-first search, breadth-first search moves horizontally across the tree first, before moving down. The image below represents the order in which we will visit each node. As you can see, we start at the root, then move down one level and visit 2 and 3. Then move down to the next level and visit 4 and 5.

To achieve this, we will be using queues. If you are not familiar with queues, check out the article below.

Let’s first create a method and call it BFS. To recreate our queue (first-in-first-out) in an easy way, we will be using an array’s push and shift methods. We will create two empty queues and call them queue and data. We will also create a node variable to keep track of the current node we are checking. Then we will start by placing the root node in the queue.

BFS() {

let queue = [];

let data = [];

let node = this.root;

queue.push(node);

}

Next, we will create a while loop to run while we have data in our queue. In this while loop, we will take the first data in our queue using shift, and store this value as our node variable. Then, we will push the value of this node into our data array. Next, we will check if the node has a left and right property. If there is, we will push these values to the queue. The while loop will continue to run while we have data in the queue. Once this completes, we will return the data list.

BFS() {

let queue = [];

let data = [];

let node = this.root;

queue.push(node);

while(queue.length) {

node = queue.shift();

data.push(node.value);

if(node.left) queue.push(node.left);

if(node.right) queue.push(node.right);

}

return data;

}

The final code will look like below. You can now create a new tree and test the different tree traversal methods we added. The returned array should list the values of the tree in a different order, according to which search order we used.

Depth-First Search and Breadth-First Search

Wrapping Up

You should now have an understanding of how we can traverse trees and visit every node once. If you are wondering when you should use one method over the other, there is a thread on Stack Overflow which I recommend reading to learn more.
['Chad Murobayashi']
['JavaScript', 'Data Structures', 'Algorithms', 'Software Development', 'Web Development']