Binary Tree Vertical Order Traversal

Updated on 13 May, 2025
Binary Tree Vertical Order Traversal header image

Problem Statement

In this problem, we are given the root of a binary tree and are required to perform a vertical order traversal of its nodes' values. Vertical order traversal means collecting the values of the tree nodes as they appear vertically, from left to right. If nodes overlap vertically, they should be listed from top to bottom, and if nodes are at the same level within the same column, they should be reported from left to right. Our task is to categorize these values column by column and provide the output in the form of a list of lists, where each sublist represents a column of the tree.

Examples

Example 1

Input:

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

Output:

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

Example 2

Input:

root = [3,9,8,4,0,1,7]

Output:

[[4],[9],[3,0,1],[8],[7]]

Example 3

Input:

root = [1,2,3,4,10,9,11,null,5,null,null,null,null,null,null,null,6]

Output:

[[4],[2,5],[1,10,9,6],[3],[11]]

Constraints

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

Approach and Intuition

Understanding how to approach the vertical order traversal of a binary tree can be broken down into the following steps:

  1. Assign a position to each node. This position will have two components:

    • Horizontal position (or x-coordinate): This changes as we move from left (-1) to right (+1) relative to the root, which has position 0.
    • Vertical position (or y-coordinate): This changes as we move from top (0) to bottom (+1) as we go down each level in the tree.
  2. Traverse the binary tree (typically using breadth-first search, BFS) and keep track of each node's coordinates and value. During this traversal:

    • Use a queue to manage nodes to be processed. Begin with the root node, initializing its position as (0,0).
    • For each node processed, capture its value in a data structure, using its x-coordinate as a key and appending its value along with its y-coordinate (to handle sorting later if multiple values fall into the same x-coordinate).
    • For each node, push its left and right children into the queue if they exist, adjusting their horizontal positions accordingly (-1 for left and +1 for right) while increasing the vertical position by 1.
  3. Once the traversal is done, sort the collected nodes:

    • First, sort by x-coordinate—that is, the columns.
    • Within each column, sort by y-coordinate to ensure values appear from top to bottom. If two nodes have the same y-coordinate, they should appear from left to right, as they would naturally by the nature of BFS.
  4. Convert the sorted structure into the desired output format:

    • Iterate through the sorted keys (x-coordinates) and gather the node values into sublists to create the final list of lists that represents the vertical order traversal.

By understanding these steps, we can effectively process and categorize the binary tree's nodes into their respective vertical columns while adhering to the constraints and properties of binary tree traversal.

Solutions

  • Java
  • Python
java
class Solution {
  // Store nodes values by columns
  Map<Integer, ArrayList<Pair<Integer, Integer>>> nodeStorage = new HashMap<>();
  int minCol = 0, maxCol = 0;

  private void runDFS(TreeNode node, Integer depth, Integer col) {
    if (node == null)
      return;

    if (!nodeStorage.containsKey(col)) {
      this.nodeStorage.put(col, new ArrayList<>());
    }

    this.nodeStorage.get(col).add(new Pair<>(depth, node.val));
    this.minCol = Math.min(minCol, col);
    this.maxCol = Math.max(maxCol, col);
    
    // Recursively call left and right subtree
    this.runDFS(node.left, depth + 1, col - 1);
    this.runDFS(node.right, depth + 1, col + 1);
  }

  public List<List<Integer>> verticalOrder(TreeNode root) {
    List<List<Integer>> output = new ArrayList<>();
    if (root == null) {
      return output;
    }

    this.runDFS(root, 0, 0);

    // Retrieve results ordered by column and sorted by row
    for (int i = minCol; i <= maxCol; ++i) {

      Collections.sort(nodeStorage.get(i), new Comparator<Pair<Integer, Integer>>() {
        @Override
        public int compare(Pair<Integer, Integer> p1, Pair<Integer, Integer> p2) {
          return p1.getKey() - p2.getKey();
        }
      });

      List<Integer> sortedColumn = new ArrayList<>();
      for (Pair<Integer, Integer> p : nodeStorage.get(i)) {
        sortedColumn.add(p.getValue());
      }
      output.add(sortedColumn);
    }

    return output;
  }
}

The code in discussion offers a solution for performing a vertical order traversal of a binary tree using Java. It utilizes a depth-first search (DFS) strategy along with a hash map to effectively categorize and store tree node values based on their horizontal distances (columns) from the root.

  • Begin by setting up a hash map nodeStorage to maintain lists of nodes grouped by their column indexes. Each list contains pairs, with each pair holding a depth and a node value.

  • Implement the runDFS method, where for each node, if not already present, a new list for the column in nodeStorage is created. Add the current node along with its depth to the respective column list. Update the minimum and maximum column values.

    • Recursively proceed to the left (column - 1) and right (column + 1) children of the current node, increasing the depth by 1.
  • In verticalOrder, initialize the output list of lists. If the root is null, return the empty list directly. Else, start the depth-first traversal from the root node positioned at depth 0, column 0.

    • After populating nodeStorage, iterate through the columns from minCol to maxCol.
    • For each column, sort its list by depth.
    • Extract the node values from sorted pairs and add them to the output.
  • Finally, return the output, which contains the node values in the order of their vertical levels from leftmost to rightmost columns.

This method ensures all nodes appearing in a vertical line are processed based on their depth, with top-down order for nodes at the same depth within the same vertical line. This efficient categorization allows for clear, structured output that accurately represents vertical levels in a binary tree.

python
from collections import defaultdict

class Solution:
    def verticalTraversal(self, node_root: TreeNode) -> List[List[int]]:
        if not node_root:
            return []

        vertical_map = defaultdict(list)
        column_min = column_max = 0

        def traverse(node, depth, columns):
            if node:
                nonlocal column_min, column_max
                vertical_map[columns].append((depth, node.val))
                column_min = min(column_min, columns)
                column_max = max(column_max, columns)

                traverse(node.left, depth + 1, columns - 1)
                traverse(node.right, depth + 1, columns + 1)

        traverse(node_root, 0, 0)

        result = []
        for col in range(column_min, column_max + 1):
            vertical_map[col].sort(key=lambda x: x[0])
            result.append([val for _, val in vertical_map[col]])

        return result

The provided Python code implements a function to perform a vertical order traversal on a binary tree. It sorts tree nodes vertically by columns and horizontally by depth using a depth-first search approach. Follow these steps to understand the implementation:

  1. Check if the input node_root is None. If it is, return an empty list early.
  2. Use a defaultdict to store nodes based on their column indices, where each key points to a list of tuples containing depth and node values.
  3. Initialize column_min and column_max to track the smallest and largest column indices encountered during the traversal.
  4. Define an inner recursive function traverse to navigate the tree. This function:
    • Updates column_min and column_max.
    • Recursively calls itself for the left child with columns - 1 and the right child with columns + 1.
  5. Initiate the traversal with traverse(node_root, 0, 0).
  6. Prepare to output the result by iterating from column_min to column_max.
  7. For each column key in vertical_map, sort the list of tuples primarily by depth (ensuring top-down order within the same column).
  8. Extract node values from the sorted tuples and store them in the result list.
  9. Finally, return the result, which contains lists of values for each vertical column.

This implementation ensures that nodes are returned in the expected vertical column order, sorted by depth, using an efficient combination of depth-first traversal and sorting by depth within columns.

Comments

No comments yet.