Binary Tree Level Order Traversal
By: Sara Khandaker
Tags: Binary Tree, Data Structures, Algorithms, Leetcode, Breadth First Search

Binary Tree Level Order Traversal

Javascript Solution to LeetCode Problem

This week my focus has been on practicing my data structures and algorithms. After completing many problems, patterns start to arise. The given problem starts to remind you of another solved problem and this makes it easier. The more you practice, the more styles of questions you will have seen, and the greater the chance you will be able to use your past experiences to help you. Here is an example of how that played out:

Problem: Binary Tree Level Order Traversal

This is a LeetCode medium difficulty problem. Given a binary tree, return the level order traversal of its nodes’ values. (ie, from left to right, level by level). For example, given the binary tree [3,9,20,null,null,15,7] :

3

/ \

9 20

/ \

15 7

return its level order traversal as:

[ [3], [9,20], [15,7]]

The following shows the definition for a binary tree node:

function TreeNode(val, left, right) {

this.val = (val===undefined ? 0 : val)

this.left = (left===undefined ? null : left)

this.right = (right===undefined ? null : right)

}

The Start to the Solution: Solve What You Can

On first look, this triggers a breadth-first search (BFS). We are visiting each node from left to right before going down a level. This is known as breadth-first traversal or level order tree traversal.

Now at this point, I have no clue how to get the node values formatted into the arrays yet. But that's ok, I have somewhere to start. I recently did another problem where I printed out the values of the nodes in a BFS. So let's write the code for that and use it as a starting point.

Breadth-First-Search (BFS) Algorithm:

Here are the steps to perform a BFS using a queue. Remember a queue works as a FIFO (first in first out).

We start by initiating a new queue modeled using an array. We then add the root node to the queue. While the queue contains node elements, we will run a while loop. First, within the loop, we want to remove the first element from the top of the queue. This will be the current node. In the case of this problem here I would simply console.log the current node’s value. This will print out the values of the binary tree in level order traversal. Lastly, we check if the node has any left or right children and add those to the queue. Make sure to add to the end of the queue to ensure FIFO.

Note: The use of an array for a queue is not the most efficient. The disadvantage of using Javascript is that it does not have a native queue implementation like C# or Java. When using an array, we go from linear to quadratic time complexity since removing the first element of an array is in itself a linear operation because of the need to shift all indices. The use of a link list for the queue implementation would be more efficient.

Code for BFS:

/* Level Order Traversal: Given a binary tree, print its nodes in level order.*/ function levelOrder(root) {

let queue = [root]

while (queue.length != 0) {

let node = queue.pop()

c onsole.log(node.val)

if (node.left) queue.unshift(node.left)

if (node.right) queue.unshift(node.right)

}

}

Solution

Now that we have access to each value in the correct order, I can focus on making the results array and the subarrays. But, how do I keep track of the levels? How do I know when I’ve gone down in depth in the binary tree?

The solution is in a second loop. A for-loop within my while loop. I want to construct an inner loop that iterates per node in a level and my outside while loop will run for each level. The number of times my for-loop runs is the same as the number of nodes at that level. This is also equal to the length of the queue at the start of the new level. Since all children of the nodes at the level above will already have been added to the queue.

Solution Algorithm:

In addition to the queue, let's also initialize the results array. Inside the while loop (which runs till the queue is empty), we define a for-loop. The for-loop will run for each of the elements in the queue at the end of the last level. This means we will increment “i” until it reaches the queue length. Before the for-loop, be sure to define the subarray for the level, this will reset before each new level. Inside the for-loop, remove a node from the top of the queue and add its value to the level subarray. Add children to the queue, if they exist. Once all the nodes of a level have been visited (the end of the for-loop) add the subarray to the results array. Continue steps until the queue is empty. Don't forget to return the results array at the end of the function!

Before we complete the algorithm we must consider edge cases:

If the root node is null we should return an empty array

Code for the Final Solution :

function levelOrder(root) {

if (!root) return []

let result = []

let queue = [root]

while (queue.length != 0) {

let subarr = []

const n = queue.length

for (let i = 0; i < n; i++) {

let node = queue.pop()

subarr.push(node.val)

if (node.left) queue.unshift(node.left)

if (node.right) queue.unshift(node.right)

}

result.push(subarr)

}

return result

}

Complexity Analysis:

Since each node is visited at least once, the time complexity is O(n). The space complexity is also O(n) since the queue can contain all the leaf nodes in the worst case.

Conclusion

Level order traversal is an important algorithm to understand as it can help solve many tech challenges. Just like how I used a problem I had solved previously to help me solve this one this solution can now give insight into further similar problems.

Next Problems:

Tags

Algorithms, Binary Tree, Breadth First Search, Data Structures, Leetcode