987 - Vertical Order Traversal of a Binary Tree
Difficulty: Hard | Pattern: BFS/DFS + Sorting | Company tags: Facebook, Amazon, Google
Problem Statement
Given the root of a binary tree, calculate the vertical order traversal of the binary tree.
For each node at position (row, col), its left and right children will be at positions (row + 1, col - 1) and (row + 1, col + 1) respectively. The root is at (0, 0).
The vertical order traversal is a list of top-to-bottom orderings for each column index from left to right. If two nodes are in the same row and column, sort by value.
Example:
Input: root = [3,9,20,null,null,15,7]
Output: [[9],[3,15],[20],[7]]
Solution: DFS + Grouping — O(n log n)
from collections import defaultdict
def verticalTraversal(root) -> list[list[int]]:
nodes = []
def dfs(node, row, col):
if not node:
return
nodes.append((col, row, node.val))
dfs(node.left, row + 1, col - 1)
dfs(node.right, row + 1, col + 1)
dfs(root, 0, 0)
# Sort by (col, row, val)
nodes.sort()
result = []
groups = defaultdict(list)
for col, row, val in nodes:
groups[col].append(val)
for col in sorted(groups.keys()):
result.append(groups[col])
return result
Algorithm Flow
Dry Run
Tree: 3(root), left=9, right=20(left=15,right=7)
Nodes collected (col, row, val):
- (0, 0, 3)
- (-1, 1, 9)
- (1, 1, 20)
- (0, 2, 15)
- (2, 2, 7)
Sorted: (-1,1,9), (0,0,3), (0,2,15), (1,1,20), (2,2,7)
Groups: col-1→[9], col0→[3,15], col1→[20], col2→[7]
Result: [[9],[3,15],[20],[7]] ✓
Key Tie-Breaking Rule
When col and row are the same, sort by value. The sort() on (col, row, val) tuples handles this automatically since Python sorts tuples lexicographically.
Complexity
- Time: O(n log n) — sorting dominates
- Space: O(n)
Key Terms
| Term | Definition |
|---|---|
| Vertical column | Set of nodes sharing the same col value computed via (row+1, col-1)/(row+1, col+1) offsets. |
| Tuple sort | Sorting (col, row, val) tuples lexicographically to resolve column order, then row order, then value tie-breaks in one pass. |
| Tie-breaking | When two nodes share (row, col), the smaller value is listed first. |
| DFS with coordinates | Recursion that threads (row, col) state through each call instead of just visiting nodes. |
| Grouping by key | Post-processing step (e.g. defaultdict(list)) that buckets sorted items by their column key. |
FAQ
- Can this be solved without sorting the full node list? Yes — use a BFS level-order traversal per column and merge columns with a min-heap keyed by
(col, row, val), but plain sorting is simpler and still O(n log n). - What if the tree is empty?
dfs(None, ...)returns immediately,nodesstays empty, and the function returns[]. - How would this differ from problem 314 (Binary Tree Vertical Order Traversal)? 314 only requires top-to-bottom BFS order within a column (no value tie-break), so a queue-based BFS with column buckets suffices without sorting by value.
- Why use DFS instead of BFS here? Either works since we collect all
(col, row, val)triples before sorting; DFS is simpler to code recursively, BFS naturally gives row order per level. - What breaks if row is omitted from the sort key? Nodes at different depths in the same column could be ordered by value alone, corrupting the required top-to-bottom sequence.
Quick Revision
- Track
(row, col)for every node starting at root(0, 0). - Left child:
(row+1, col-1); right child:(row+1, col+1). - Collect
(col, row, val)triples via DFS or BFS. - Sort triples lexicographically — this handles column, then row, then value tie-break in one shot.
- Group sorted triples by
colinto the final list of lists. - Return columns ordered from leftmost (smallest col) to rightmost.
- Time is dominated by sorting: O(n log n); space is O(n) for the triples list.
- Contrast with problem 314, which skips the value tie-break and can use BFS + column dict alone.
Related Problems
- 314 - Binary Tree Vertical Order Traversal — same column/row coordinate idea without the value tie-break.
- 102 - Binary Tree Level Order Traversal — shares the BFS-by-level pattern used to derive row values.
- 199 - Binary Tree Right Side View — another traversal problem keyed on row/depth bookkeeping.