Binary Tree Level Order Traversal

Updated on 12 May, 2025
Binary Tree Level Order Traversal header image

Problem Statement

Given a binary tree's root node, the task is to perform a level order traversal on it. This traversal involves visiting all the nodes at the current depth level from left to right before moving to the next level in the tree.

Examples

Example 1

Input:

root = [3,9,20,null,null,15,7]

Output:

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

Example 2

Input:

root = [1]

Output:

[[1]]

Example 3

Input:

root = []

Output:

[]

Constraints

  • The number of nodes in the tree is in the range [0, 2000].
  • -1000 <= Node.val <= 1000

Approach and Intuition

The level order traversal of a binary tree, also known as breadth-first traversal, requires the traversal of nodes level by level, progressing from left to right at each level. Here's a step-by-step approach and the intuition behind it:

  1. Initial Check:

    • Check if the root is null. If it is, return an empty list because there are no nodes to traverse.
  2. Queue Utilization:

    • Utilize a queue to keep track of nodes at the current level. Initially, add the root to this queue.
  3. Traversal:

    • While the queue is not empty:
      • Determine the number of nodes at the current level (size of the queue).
      • For each node in the current level:
        • Add it to a temporary list (current level).
        • Enqueue its children (left then right) if they exist.
      • After processing all nodes at the current level, add the temporary list to the final result list.
  4. Result Compilation:

    • Continue the process until the queue is empty and all levels are processed.
    • Return the compiled list of levels.

This method ensures that all nodes on the same level are processed together, and each level is handled before moving onto the next. The constraints given allow the method to handle trees of size up to 2000 nodes comfortably, with values ranging from -1000 to 1000.

Solutions

  • C++
  • Java
  • C
  • JavaScript
  • Python
cpp
class Solution {
public:
    vector<vector<int>> traverseLevels(TreeNode* root) {
        vector<vector<int>> result;
        if (!root) return result;
        deque<TreeNode*> deque;
        deque.push_back(root);
        int currentLevel = 0;
        while (!deque.empty()) {
            result.push_back(vector<int>());
            int nodesAtLevel = deque.size();
            for (int i = 0; i < nodesAtLevel; ++i) {
                TreeNode* currentNode = deque.front();
                deque.pop_front();
                result[currentLevel].push_back(currentNode->val);
                if (currentNode->left) deque.push_back(currentNode->left);
                if (currentNode->right) deque.push_back(currentNode->right);
            }
            currentLevel++;
        }
        return result;
    }
};

The provided C++ code defines a method traverseLevels that performs a level-order traversal on a binary tree. The method returns a two-dimensional vector containing the values of the nodes at each level of the binary tree:

  • The function starts by checking if the root is nullptr, returning an empty result if true.
  • It then initializes a deque to store the nodes of the tree and a result vector of vector of integers to keep the values.
  • The root node gets added to the deque initially.
  • A while loop runs as long as the deque is not empty. Inside the loop:
    1. A new vector is added to result to store the values of nodes at the current level.
    2. The number of nodes at the current level is determined using the size of the deque.
    3. A for loop runs for the number of nodes at that level:
      • It pops nodes from the front of the deque.
      • The values of these nodes are added to the current level's vector in result.
      • If the left or right child of the node exists, they are added to the back of the deque to be processed in the coming iterations.
    4. The currentLevel counter is incremented after processing each level.
  • The function ultimately returns the result, which contains the level order traversal of the binary tree.

This C++ implementation efficiently maps out the nodes of a binary tree by levels using a deque for optimal access and insertion operations.

java
class Solution {
    public List<List<Integer>> traverseByLevels(TreeNode root) {
        List<List<Integer>> allLevels = new ArrayList<>();
        if (root == null) return allLevels;

        Queue<TreeNode> nodeQueue = new LinkedList<>();
        nodeQueue.add(root);
        int currentLevel = 0;
        while (!nodeQueue.isEmpty()) {
            allLevels.add(new ArrayList<Integer>());

            int levelSize = nodeQueue.size();
            for (int i = 0; i < levelSize; ++i) {
                TreeNode current = nodeQueue.poll();

                allLevels.get(currentLevel).add(current.val);

                if (current.left != null) nodeQueue.add(current.left);
                if (current.right != null) nodeQueue.add(current.right);
            }
            currentLevel++;
        }
        return allLevels;
    }
}

The provided Java method traverseByLevels performs a level order traversal on a binary tree. This method returns a List<List<Integer>>, where each nested list contains the values of the tree nodes at each depth level of the tree.

Follow these steps to understand how the solution works:

  1. Initialize an empty list allLevels to store the values of nodes at each level.
  2. Check if the root node is null. If so, return the empty allLevels list immediately as there are no nodes to process.
  3. Use a Queue<TreeNode> to help with the level order traversal. Begin by enqueueing the root node.
  4. Declare a variable currentLevel to keep track of the tree depth during traversal.
  5. Use a while loop to process nodes as long as there are any left in the queue.
    1. Add a new list into allLevels to prepare for storing node values of the current level.
    2. Calculate the number of nodes at the current level with levelSize which is equal to the number of nodes in the queue at the start of the loop.
    3. Loop over each node at the current level:
      1. Dequeue the node from the front of the queue.
      2. Add the node's value to the current level list inside allLevels.
      3. Enqueue the child nodes (left first, then right) of the current node if they are not null.
    4. Increment currentLevel to move to the next level in subsequent iterations.
  6. Once the queue is empty, return the allLevels which now contains all node values organized level by level.

This method efficiently traverses each node once and processes all nodes level by level using a queue, adhering to the standard breadth-first searching technique in tree data structures.

c
int** bfsTraversal(struct TreeNode* root, int* returnSize, int** returnColumnSizes) {
    *returnSize = 0;
    if (!root) return NULL;

    typedef struct Element {
        void* content;
        struct Element* next;
    } Element;

    typedef struct SimpleQueue {
        Element* first;
        Element* last;
    } SimpleQueue;

    SimpleQueue* initializeQueue() {
        SimpleQueue* q = malloc(sizeof(SimpleQueue));
        q->first = q->last = NULL;
        return q;
    }

    void enqueue(SimpleQueue* q, void* content) {
        Element* elem = malloc(sizeof(Element));
        elem->content = content;
        elem->next = NULL;
        if (q->first == NULL) {
            q->first = q->last = elem;
        } else {
            q->last = q->last->next = elem;
        }
    }

    void* dequeue(SimpleQueue* q) {
        if (!q->first) return NULL;
        Element* tmp = q->first;
        void* content = tmp->content;
        q->first = q->first->next;
        if (q->first == NULL) q->last = NULL;
        free(tmp);
        return content;
    }

    size_t queueSize(SimpleQueue* q) {
        size_t count = 0;
        for (Element* elem = q->first; elem != NULL; elem = elem->next) {
            count++;
        }
        return count;
    }

    int queueIsEmpty(SimpleQueue* q) {
        return q->first == NULL;
    }

    SimpleQueue* queue = initializeQueue();
    enqueue(queue, root);
    int** treeLevels = malloc(sizeof(int*) * 2000);
    *returnColumnSizes = (int*)malloc(sizeof(int) * 2000);

    while (!queueIsEmpty(queue)) {
        int nodesAtLevel = queueSize(queue);
        treeLevels[*returnSize] = malloc(sizeof(int) * nodesAtLevel);
        (*returnColumnSizes)[*returnSize] = nodesAtLevel;
        for (int i = 0; i < nodesAtLevel; i++) {
            struct TreeNode* node = dequeue(queue);
            treeLevels[*returnSize][i] = node->val;
            if (node->left) enqueue(queue, node->left);
            if (node->right) enqueue(queue, node->right);
        }
        (*returnSize)++;
    }

    return treeLevels;
}

The provided C code performs a breadth-first search (BFS) on a binary tree to return the values of the nodes level by level. This approach uses a custom queue structure to manage the nodes during traversal. Here's how it works:

  • Initializes a returnSize to keep track of how many levels in the tree have been processed.
  • Checks if the input root is NULL. If so, returns NULL.
  • Defines a SimpleQueue and Element to create a basic queue structure capable of handling generic content.
  • Implements helper functions for the queue: initializeQueue to create a new queue, enqueue and dequeue to add and remove elements, queueSize to determine the current size of the queue, and queueIsEmpty to check if the queue is empty.
  • Begins the BFS:
    • Initializes the queue and enqueues the root node.
    • Uses an array treeLevels to store the node values for each level.
    • Dynamically allocates an array returnColumnSizes to store the number of nodes at each level.
    • Processes each level of the tree by dequeueing all nodes at the current level, storing their values, and enqueueing their children.
    • The level processing continues until the queue is empty.
  • The function finally returns treeLevels, an array of pointers where each pointer points to an array containing the values of the nodes at that level.

This solution efficiently captures the hierarchical structure of a binary tree using a level order traversal, which is particularly useful for tasks that require knowledge of the tree structure beyond simple depth-first traversals. The use of dynamic memory management and custom data structures ensures flexibility and thorough traversal of the tree.

js
var bfsTraversal = function (rootNode) {
    var result = [];
    if (!rootNode) return result;
    var nodeQueue = [rootNode];
    var currentLevel = 0;
    while (nodeQueue.length > 0) {
        result.push([]); 
        var levelSize = nodeQueue.length;
        for (var i = 0; i < levelSize; i++) {
            var currentNode = nodeQueue.shift(); 
            result[currentLevel].push(currentNode.val);
            if (currentNode.left) nodeQueue.push(currentNode.left);
            if (currentNode.right) nodeQueue.push(currentNode.right);
        }
        currentLevel++;
    }
    return result;
};

The provided JavaScript function, bfsTraversal, implements a breadth-first search (BFS) strategy to perform a level order traversal on a binary tree. This is a commonly used technique to visit all the nodes of a binary tree level by level from left to right. Here's a breakdown on how this function works:

  • Initialize an empty array result to store the nodes at each level.
  • If the root node is null, return the empty result array immediately; this handles the edge case where the binary tree is empty.
  • Use an array nodeQueue to keep track of nodes to visit, starting with the root node.
  • The variable currentLevel is used to track the depth of traversal into the binary tree.

The main computational work happens inside a while loop, which continues as long as there are nodes to process in nodeQueue:

  1. An empty sublist is added to result for the current level.
  2. The current level's size is determined by nodeQueue.length, which indicates how many nodes need to be processed at this level.
  3. A for loop iterates over each node in the current level:
    • The node at the front of the queue is removed and processed.
    • The value of the current node is added to the corresponding level list in result.
    • If the current node has a left child, it is added to nodeQueue to be processed in the next level. Similarly, if there's a right child, it is also added.
  4. Increment currentLevel to move to the next level in the binary tree.

The function concludes by returning the result, which contains lists of node values at each level of the binary tree.

This code efficiently handles different scenarios, including trees of varying sizes and shapes, by adapting to the breadth and depth dynamically. Additionally, the straightforward use of list operations for queues makes the code easy to read and understand, utilizing JavaScript's native array functionalities.

Keep this approach in mind when you need to handle hierarchical data structures like trees, as BFS is often a suitable choice for evenly exploring each level. This function can be seamlessly integrated into larger applications or used in standalone form to process tree-based data efficiently.

python
from collections import deque

class Solution:
    def traverseBinaryTree(self, root: TreeNode) -> List[List[int]]:
        result = []
        if not root:
            return result

        current_level = 0
        queue = deque([root])
        while queue:
            result.append([])
            num_nodes = len(queue)

            for _ in range(num_nodes):
                current_node = queue.popleft()
                result[current_level].append(current_node.val)

                if current_node.left:
                    queue.append(current_node.left)
                if current_node.right:
                    queue.append(current_node.right)

            current_level += 1

        return result

The Python code provided performs a level order traversal on a binary tree using the deque from the collections module for efficient queue operations. Here's an overview of the solution's implementation:

  1. Initialize an empty list, result, to store the traversal results.
  2. Check if the root is None; if true, return an empty list.
  3. Initialize a current_level variable to track the current tree level starting from 0.
  4. Create a queue and enqueue the root node to start the traversal.
  5. Use a while loop to process nodes while there are nodes in the queue:
    • Begin each new level by appending an empty list to result.
    • Determine the number of nodes, num_nodes, at the current tree level.
    • Iterate through these nodes using a for loop:
      • Dequeue a node from the front of the queue.
      • Append its value to the list corresponding to current_level.
      • Enqueue the left and right children to the queue, if they exist.
    • Increment current_level to move to the next level.
  6. Finally, return the result, which contains the values of the tree nodes organized by their levels.

This approach ensures that each level of the tree is processed separately and that nodes at the same level are processed together in order, resulting in a grouped list of values by tree depth. The use of a deque for the queue minimizes the time complexity associated with frequently adding and removing elements from the queue.

Comments

No comments yet.