The O(n) Club: Vertical Order Traversal of a Binary Tree — Columns, Chaos, and Why BFS Wins
⚡ TL;DR
Want to print a binary tree column by column (left to right, top to bottom within columns)? Ignore DFS daydreams—BFS plus a Map is your BFF. Here’s the gossipy Java skeleton:
// BFS vertical order quickie Queue<Pair<TreeNode, Integer>> queue = new LinkedList<>(); Map<Integer, List<Integer>> colTable = new HashMap<>(); queue.offer(new Pair(root, 0)); while (!queue.isEmpty()) { Pair<TreeNode, Integer> entry = queue.poll(); // add entry.getKey().val to colTable.get(entry.getValue()) // add kids with col-1, col+1 }
🧠 How Devs Usually Mess This Up
Confession: Most devs try DFS and then spend an afternoon playing whack-a-bug with the ordering rules. DFS explores all the wrong places at the wrong times, so your verticals come out organized like my desk (read: not at all). People also blunder by:
- Forgetting columns can be negative, aka, the left wing of your tree doesn’t vanish into the upside down.
- Ignoring that nodes at the same spot (column & row) may need order-by-value magic.
- Confusing different specs—sometimes it’s BFS order, sometimes value-order per column. Interviewers love variety, right?
🔍 What’s Actually Going On
Your binary tree’s a mini Manhattan. The root stands at column 0, row 0, owning Central Park. Go left? That’s column-1, one row down. Go right? You guessed it: column+1. Nodes stack in columns; rows (a.k.a. distance from the root) sort top-to-bottom. If you want vertical order that doesn’t look like alphabet soup, BFS walks floor-by-floor, so nodes at the same level get listed in glorious left-to-right order. Use a Map: column index → list of values. Blend with BFS for level order, and you’ve got skyline architecture worthy of LeetCode Zillow.
🛠️ PseudoCode
- Create a HashMap: key = column, value = list of values at that column
- Slap the root into a BFS queue with column 0
- Track minCol and maxCol as you go (trust me, you’ll need them for output range)
- BFS loop: for each node, drop its value into the column’s list, add left child with col-1, right child with col+1. Tweak min/max col champs.
- After the loop, walk from minCol to maxCol; that’s your output
// Java pseudo-for-humans
Map<Integer, List<Integer>> columns = new HashMap<>();
Queue<Pair<TreeNode, Integer>> queue = new LinkedList<>();
int min = 0, max = 0;
queue.offer(new Pair(root, 0));
while (!queue.isEmpty()) {
var p = queue.poll();
TreeNode node = p.getKey();
int col = p.getValue();
columns.computeIfAbsent(col, k -> new ArrayList<>()).add(node.val);
min = Math.min(min, col); max = Math.max(max, col);
if(node.left != null) queue.offer(new Pair(node.left, col-1));
if(node.right != null) queue.offer(new Pair(node.right, col+1));
}
for(int i = min; i <= max; i++) output.add(columns.get(i));
💻 The Code
// Full Java solution
class Solution {
public List<List<Integer>> verticalOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
if (root == null) return result;
Map<Integer, List<Integer>> colMap = new HashMap<>();
Queue<Pair<TreeNode, Integer>> queue = new LinkedList<>();
queue.offer(new Pair(root, 0));
int min = 0, max = 0;
while (!queue.isEmpty()) {
Pair<TreeNode, Integer> p = queue.poll();
TreeNode node = p.getKey();
int col = p.getValue();
colMap.computeIfAbsent(col, k -> new ArrayList<>()).add(node.val);
min = Math.min(min, col);
max = Math.max(max, col);
if (node.left != null) queue.offer(new Pair(node.left, col-1));
if (node.right != null) queue.offer(new Pair(node.right, col+1));
}
for (int i = min; i <= max; i++) {
result.add(colMap.get(i));
}
return result;
}
}
// Use javafx.util.Pair or roll your own simple Pair class
⚠️ Pitfalls, Traps & Runtime Smacks
- Overlapping nodes (same column, same row): Some specs say to sort by value—so if that comes up, group (row, value) pairs then sort by row then by value.
- Off-by-one column handling: Your far-left column might be -17. Don’t start at 0 by default.
- DFS trickery: DFS is faster to type, slower to debug in this case.
- Complexity: O(n) overall if you trust your map, plus a smidge for final sorting if column values overlap.
🧠 Interviewer Brain-Tattoo
“If you treat DFS like duct-tape, expect leaks. For columnar order, BFS is the city planner; DFS is the guy drawing random doodles on the blueprint.”