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.

This extends the column idea from Binary Tree Top/Bottom View. There, each column reported a single node — the shallowest or the deepest. Here every node is reported, so a column becomes an ordered list, and the interesting work moves into deciding that order.

Each node gets 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). The output is then: group by x, sort the groups by x, and within each group sort by y, breaking ties by value.

Why the traversal cannot do the sorting for you

It is tempting to reach straight for the level-by-level walk and assume it delivers the right order for free. A breadth-first walk does visit rows in increasing order, so nodes land in each column already sorted by row — that much is genuinely free.

The trap is the tie. Two nodes can share a column and a row: in a full tree of seven nodes, node 2's right child and node 3's left child both sit in column 0 at row 2. A left-to-right breadth-first walk visits them in tree order, which is not the required order. The problem says these must be sorted by value, and value has no relationship at all to where a node sits. No traversal order can produce that; it has to be imposed afterwards.

Key Insightonce ties are broken by something the traversal does not know about — a value, a name, a timestamp — the traversal stops being a sorting mechanism and becomes purely a way of collecting data. At that point the honest move is to gather every node with its full coordinates and sort explicitly, rather than trying to arrange the walk so the answer falls out.
Collect triples, then sort once

So walk the tree in any order at all — depth-first is fine, and simpler here — recording a triple (x, y, value) for every node. Then sort the whole collection by x, then y, then value, and cut it into groups wherever x changes. That single sort satisfies all three ordering rules at once, because sorting by a tuple compares the first component first and only consults the later ones on ties — exactly the precedence the problem describes.

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 — in 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. Sorting by value first, or by row before column, produces output that looks plausible on symmetric examples and is wrong on the ones that matter. It is worth writing the key out explicitly and checking it against the problem's own wording — column, then row, then value — rather than relying on whatever order the tuple happened to be built in.
Worked example:root 1, children 2 and 3; 2 has children 4 and 5; 3 has children 6 and 7
Coordinates: 1 at (0, 0). 2 at (-1, 1), 3 at (1, 1). 4 at (-2, 2), 5 at (0, 2), 6 at (0, 2), 7 at (2, 2).

Note 5 and 6 — they land on the identical spot, arriving from opposite subtrees, because 5 went left-then-right and 6 went right-then-left.

Sorted by (column, row, value):
- (-2, 2, 4)
- (-1, 1, 2)
- (0, 0, 1), then (0, 2, 5), then (0, 2, 6) — the first two are separated by row; the last two tie on row and are separated by value.
- (1, 1, 3)
- (2, 2, 7)

Cutting on column changes gives [[4], [2], [1, 5, 6], [3], [7]]. Had 5 and 6 held the values 9 and 5 instead, the column would read [1, 5, 9] — the values decide, not which subtree the node came from.

Cost, and where this sits

The walk is O(N); the sort dominates at O(N log N) time, with O(N) space for the collected triples. It is possible to avoid the global sort by bucketing into a map keyed by column and sorting each column separately, but the worst case is unchanged when one column holds most of the nodes.

The lesson is a small but useful piece of judgement: check whether the required ordering is entirely determined by structure, and if it is not, stop trying to be clever with the traversal. Top and bottom views could be resolved by arrival order because depth alone decided the winner. This problem's value tie-break lives outside the structure, so an explicit sort is not a failure to find the elegant solution — it is the correct one. Recognising which side of that line a problem falls on saves a lot of debugging.

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