Binary Tree Level Order Traversal II

MEDIUM

Description

Given the root of a binary tree, return the bottom-up level order traversal of its nodes' values. (i.e., from left to right, level by level from leaf to root).

 

Example 1:

Input: root = [3,9,20,null,null,15,7]
Output: [[15,7],[9,20],[3]]

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

Approaches

Checkout 3 different approaches to solve Binary Tree Level Order Traversal II. Click on different approaches to view the approach and algorithm in detail.

Depth-First Search (DFS) with Reversal

This approach uses a recursive Depth-First Search (DFS) to traverse the tree. It keeps track of the level of each node to group them correctly. The levels are gathered in a top-down order, and the final list of levels is reversed to achieve the required bottom-up order.

Algorithm

  • Initialize an empty list of lists, result.
  • If the root is null, return the empty result.
  • Create a recursive helper function, dfs(node, level, result).
  • Call the helper function with the root, level 0, and the result list.
  • Inside the helper function:
    • Base case: if the node is null, return.
    • If level is equal to result.size(), it's the first time we're visiting this level. Add a new empty list to result.
    • Add the node's value to the list at index level: result.get(level).add(node.val).
    • Recursively call for the left child: dfs(node.left, level + 1, result).
    • Recursively call for the right child: dfs(node.right, level + 1, result).
  • After the initial DFS call returns, the result list contains the levels in top-down order.
  • Reverse the result list using Collections.reverse().
  • Return the reversed list.

We can perform a pre-order traversal on the tree while keeping track of the current level. We use a list of lists, result, to store the nodes for each level. The level variable acts as an index into this result list. When we visit a node at a level for the first time, we add a new list to result. Then, we add the node's value to the list corresponding to its level. After the traversal is complete, result will hold the levels from top to bottom. The final step is to reverse this result list to get the bottom-up order.

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public List<List<Integer>> levelOrderBottom(TreeNode root) {
        List<List<Integer>> result = new ArrayList<>();
        if (root == null) {
            return result;
        }
        dfs(root, 0, result);
        Collections.reverse(result);
        return result;
    }

    private void dfs(TreeNode node, int level, List<List<Integer>> result) {
        if (node == null) {
            return;
        }
        // If we are visiting a new level, add a new list for it.
        if (level >= result.size()) {
            result.add(new ArrayList<>());
        }
        // Add the node's value to the list for its level.
        result.get(level).add(node.val);
        
        // Recurse for children.
        dfs(node.left, level + 1, result);
        dfs(node.right, level + 1, result);
    }
}

Complexity Analysis

Time Complexity: O(N)Space Complexity: O(N)

Pros and Cons

Pros:
  • Conceptually simple if one is familiar with DFS and recursion.
  • Follows a standard tree traversal pattern.
Cons:
  • Requires an extra step to reverse the final list.
  • The recursive approach can lead to a StackOverflowError for very deep or skewed trees.
  • The space complexity for the recursion stack is O(H), where H is the tree height, which can be O(N) for a skewed tree.

Code Solutions

Checking out 4 solutions in different languages for Binary Tree Level Order Traversal II. Click on different languages to view the code.

/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val = val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val = val; * this.left = left; * this.right = right; * } * } */ class Solution { public List < List < Integer >> levelOrderBottom ( TreeNode root ) { LinkedList < List < Integer >> ans = new LinkedList <>(); if ( root == null ) { return ans ; } Deque < TreeNode > q = new LinkedList <>(); q . offerLast ( root ); while (! q . isEmpty ()) { List < Integer > t = new ArrayList <>(); for ( int i = q . size (); i > 0 ; -- i ) { TreeNode node = q . pollFirst (); t . add ( node . val ); if ( node . left != null ) { q . offerLast ( node . left ); } if ( node . right != null ) { q . offerLast ( node . right ); } } ans . addFirst ( t ); } return ans ; } }

Video Solution

Watch the video walkthrough for Binary Tree Level Order Traversal II



Algorithms:

Breadth-First Search

Data Structures:

TreeBinary Tree

Subscribe to Scale Engineer newsletter

Learn about System Design, Software Engineering, and interview experiences every week.

No spam, unsubscribe at any time.