Algorithm

Vertical Order Traversal

Trees Pattern

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.

CONSTRAINTS
  • 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
EXAMPLE 1
Input: root = [3,9,20,null,null,15,7]
Output: [[9],[3,15],[20],[7]]
Columns -1 through 2. Column 0 holds the root at row 0 and 15 at row 2, so the shallower one comes first — and 15 arrives there from a different subtree entirely.
EXAMPLE 2
Input: root = [1,2,3,4,5,6,7]
Output: [[4],[2],[1,5,6],[3],[7]]
5 and 6 share column 0 and row 2. Neither is 'more left' in any meaningful sense, so the value tie-break decides, putting 5 before 6.
EXAMPLE 3
Input: root = [3,1,4,0,2,2,null]
Output: [[0],[1],[3,2,2],[4]]
Two nodes both holding 2 land in column 0 at the same row. Equal values order either way, but the tie-break must still be applied consistently or the surrounding entries can shift.
EXAMPLE 4
Input: root = [1]
Output: [[1]]
A single column containing a single node. The outer list holds one group, not the bare value.
What if two nodes share the same row and column?
Order them by value, ascending. This is the rule that separates the problem from a plain column grouping, and it cannot be satisfied by traversal order alone.
Can nodes from different subtrees really collide?
Yes. A right-then-left path and a left-then-right path both return to the starting column at the same depth, so collisions are common rather than exotic.
Does a left-to-right breadth-first walk resolve ties correctly?
No, and assuming it does is the classic wrong answer here. It orders colliding nodes by their position in the tree rather than by value, which the problem explicitly overrides.
Must the columns be sorted?
Yes, leftmost first, and column numbers can be negative — so an explicit ordering step is required.

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).

text
                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]]
Why the traversal can't do the sorting

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.

Key Insightonce ties break on something the traversal doesn't know — a value, a name, a timestamp — the traversal stops being a sorter and is only a collector. Gather every node with its full coordinates and sort explicitly.
Collect triples, sort once

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.

python
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 out
Crucial Notethe sort key must be the triple in exactly this order — column, then row, then value. Sorting by value first, or row before column, looks plausible on symmetric examples and is wrong on the ones that matter. Write the key out and check it against the wording.
Watch it

Coordinates for the tree above, then sorted:

text
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.

Cost, and the judgement

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.

Interactive Strategy Visualization

Vertical Batching

Full Spectrum Traversal Strategy

COORD DISCOVERY
Use BFS to track HD (x) and Level (y).
MAP GROUPING
Group nodes by HD in a TreeMap.
RESULT FLATTEN
Sort by x then y, then return list.
HD -1
HD 0
HD 1

Note: For nodes at the same HD and Level, Vertical Order traditionally sorts them by value.

O(N) BFS Grouping (Wrong On Value Ties)
O(N log N) Time · O(N) Space Collect Coordinates And Sort