Binary Tree Top/Bottom View
Draw the tree so the root sits at horizontal position 0, every left step moves one unit left and every right step one unit right. Nodes sharing a position line up in the same vertical column. The top view is the shallowest node of each column; the bottom view is the deepest. Return the chosen values ordered by column from leftmost to rightmost. An empty tree returns an empty list.
- The number of nodes is in the range [0, 100]
- -100 <= Node.val <= 100
- Columns are ordered left to right in the output
- Exactly one value per non-empty column
root = [1,2,3,4,5,6,7], top view[4,2,1,3,7]root = [1,2,3,4,5,6,7], bottom view[4,5,6,3,7]root = [1,2,3,null,4], top view[2,1,3]root = [1], top view[1]Both views ask the same question with different tie-breaks: group the nodes into vertical columns, then pick one node from each column — the highest for the top view, the lowest for the bottom view.
The word "column" is what needs defining, because a tree's node has no built-in horizontal coordinate. Give it one: the root sits at 0, stepping left subtracts 1, stepping right adds 1. Two nodes with the same number line up vertically no matter how far apart they are in the tree. This is the same move as in Maximum Width of Binary Tree — invent a coordinate so a spatial question becomes arithmetic — except that there the position had to distinguish every seat, while here left and right steps deliberately cancel so that different subtrees can share a column.
That cancellation is the source of all the difficulty. A node reached by going left-then-right ends up in the same column as the root, so a column can gather nodes from wildly different parts of the tree, and there is no way to walk a column directly.
So collect rather than search. Walk the whole tree, and as each node is visited, file it under its column number in a map from position to value. Every node is filed exactly once; the only question is what happens when a column already has an entry.
For those rules to be correct, "first filed" must mean "shallowest." A breadth-first walk gives exactly that: it visits all of depth 0, then all of depth 1, and so on, so arrival order is depth order. Under a depth-first walk it is not — a node five levels down the left subtree is visited long before the root's right child — and the top-view rule would silently pick the wrong node. Breadth-first is not a stylistic choice here; it is what makes the tie-break sound.
if root is None: return []
col = {} # position -> chosen value
queue = deque([(root, 0)]) # each entry: a node and its column
while queue:
node, x = queue.popleft()
if is_top:
if x not in col: # first arrival = shallowest = visible from above
col[x] = node.val
else:
col[x] = node.val # last arrival = deepest = visible from below
if node.left: queue.append((node.left, x - 1))
if node.right: queue.append((node.right, x + 1))
return [col[x] for x in sorted(col)] # columns, leftmost firstsorted is not optional and not free. The map's keys are column numbers that may be negative and arrive in no particular order, so the output must be ordered explicitly — forgetting this produces the right values in an arbitrary arrangement, which is a hard bug to spot on small symmetric examples. Sorting costs O(K log K) for K columns; if that matters, track the smallest and largest column seen during the walk and read the map out from min to max, which is O(K) since every column between two occupied columns is itself occupied.Now the bottom view on the same tree. Every visit overwrites, so column 0 is claimed by 1, then overwritten by 5, then by 6 — leaving 6, the last of the two deepest nodes in that column. The result is [4, 5, 6, 3, 7]. Note that 5 and 6 are at the same depth in the same column; the queue's left-to-right order is what makes 6 the winner, and this is exactly the ambiguity worth raising with an interviewer.
Every node is enqueued and dequeued once with constant work: O(N) for the walk. Sorting the columns adds O(K log K) where K is the number of distinct columns, at most N. Space is O(N) for the map plus O(W) for the queue.
The skeleton to lift from this problem is coordinate, collect, resolve: give each node a coordinate that captures the geometry the question cares about, group nodes by it, then apply a tie-break within each group. Once framed that way, a whole family follows immediately — top view keeps the minimum depth per column, bottom view the maximum, and Vertical Order Traversal, coming next, keeps all of them sorted within each column, which is why it needs a more careful tie-break than either view here.
Vertical Projection
Toggle between Top and Bottom views
Column projection
Project every node onto a horizontal x-axis (HD).