Vertical Order Traversal
Place the root at coordinates (row 0, column 0); a left child is one row down and one column left, a right child one row down and one column right. Group every node by column and return the groups ordered from the leftmost column to the rightmost. Within a column, nodes are ordered by row, top to bottom; and if two nodes share both row and column, they are ordered by value, smallest first. An empty tree returns an empty list.
- The number of nodes is in the range [0, 1000]
- 0 <= Node.val <= 1000
- Columns are output left to right; within a column, rows run top to bottom
- Nodes sharing a row and column are ordered by value ascending
root = [3,9,20,null,null,15,7][[9],[3,15],[20],[7]]root = [1,2,3,4,5,6,7][[4],[2],[1,5,6],[3],[7]]root = [3,1,4,0,2,2,null][[0],[1],[3,2,2],[4]]root = [1][[1]]Group the nodes into vertical columns and report every node in each column, ordered top-to-bottom by row — and when two nodes share the same row and column, order them by value.
Give each node two coordinates: a column x (root 0, left child x−1, right child x+1) and a row y (root 0, both children y+1).
1 (x0, y0)
/ \
2 (-1,1) 3 (1,1)
/ \ / \
4(-2,2) 5(0,2) 6(0,2) 7(2,2)
5 and 6 land on the SAME spot (col 0, row 2), from opposite subtrees
output: [[4], [2], [1, 5, 6], [3], [7]]A level-by-level (BFS) walk visits rows in increasing order, so nodes land in each column already sorted by row — that part is free. The trap is the tie: 5 and 6 share column 0 and row 2 (one went left-then-right, the other right-then-left, both landing back at column 0). A left-to-right BFS visits them in tree order — but the problem says order them by value, which has nothing to do with position. No traversal order can produce that; it must be imposed afterward.
Walk in any order (depth-first is simplest here), recording a triple (x, y, value) per node. Sort the whole list by x, then y, then value — a tuple sort compares the first field first and only consults later ones on ties, exactly the precedence wanted — then cut into groups wherever x changes.
nodes = []
def walk(node, x, y):
if node is None: return
nodes.append((x, y, node.val)) # column, row, value
walk(node.left, x - 1, y + 1)
walk(node.right, x + 1, y + 1)
walk(root, 0, 0)
nodes.sort() # by x, then y, then value — that priority
out = []
for x, _, val in nodes:
if not out or x != prev_x: # a new column starts a new group
out.append([])
prev_x = x
out[-1].append(val)
return outCoordinates for the tree above, then sorted:
1(0,0) 2(-1,1) 3(1,1) 4(-2,2) 5(0,2) 6(0,2) 7(2,2)
sort by (col, row, value):
(-2,2,4)
(-1,1,2)
(0,0,1) (0,2,5) (0,2,6) <- 1 first by row; then 5 before 6 by VALUE
(1,1,3)
(2,2,7)
cut on column change -> [[4], [2], [1, 5, 6], [3], [7]]Had 5 and 6 instead held 9 and 5, that column would read [1, 5, 9] — the values decide, not which subtree the node came from.
The walk is O(N); the sort dominates at O(N log N), with O(N) space for the triples. The judgement to keep: if the required ordering isn't fully determined by structure, stop trying to be clever with the traversal — collect coordinates and sort. A tie-break that lives outside the tree (like value) means an explicit sort isn't a failure to find the elegant walk; it is the answer.
Vertical Batching
Full Spectrum Traversal Strategy
Note: For nodes at the same HD and Level, Vertical Order traditionally sorts them by value.