Advertisement

Right/Left view of Binary Tree

Left & Right View of Binary Tree — Brute Force vs Optimal
Left View & Right View · Brute Force vs Optimal · C++

What you see from the sides

The left view of a binary tree is the set of nodes visible when looking at it from the left side — the first node at each level. The right view is the same from the right — the last node at each level. Two approaches: brute force (BFS + pick first/last) and optimal (DFS with level tracking).

01

Brute Force — the tree and the idea

Brute Force · O(N) time · O(N) space

The brute force approach uses BFS (level-order traversal) to collect all nodes level by level, then simply picks the first element of each level for the left view and the last element of each level for the right view. The tree used in the brute force code:

Brute Force Tree
1 2 3 4 5
levelOrder — BFS
1queue q;
2q.push(root);
3
4while (!q.empty()) {
5    int size = q.size();
6    vector<int> level;
7    for (int i = 0; i < size; i++) {
8        Node* node = q.front(); q.pop();
9        level.push_back(node->data);
10       if (node->left)  q.push(node->left);
11       if (node->right) q.push(node->right);
12   }
13   ans.push_back(level);
14}

Expected: Left View = [1, 2, 4] · Right View = [1, 3, 5]

02

Brute Force — BFS produces the level-order table

The BFS queue processes nodes level by level. At each iteration of the outer while loop, it captures the current queue size (= number of nodes at this level), then processes exactly that many nodes. Children are enqueued for the next level. The result is a 2D vector — one sub-vector per level.

Tree with levels marked
Level 0 Level 1 Level 2 1 2 3 4 5
Level Nodes (left → right) First (left view) Last (right view)
0 1 1 1
1 2 → 3 2 3
2 4 → 5 4 5
levelOrder — how each level is captured
1// Iteration 1: size=1 → process node 1 → level=[1] → push 2,3
2// Iteration 2: size=2 → process 2,3 → level=[2,3] → push 4,5
3// Iteration 3: size=2 → process 4,5 → level=[4,5] → no children
4
5ans = { {1}, {2,3}, {4,5} }   // 2D vector: one row per level
03

Brute Force — pick first and last of each level

Once the full level-order traversal is stored, extracting the views is trivial. The left view takes level[0] from each row; the right view takes level.back(). Simple — but it required storing the entire 2D traversal in memory, even though only one node per level is needed.

Left View · level[0] per row
1 2 4
{1} → first of [1]
{1,2} → first of [2,3]
{1,2,4} → first of [4,5]
Right View · level.back() per row
1 3 5
{1} → last of [1]
{1,3} → last of [2,3]
{1,3,5} → last of [4,5]
leftView & rightView — extraction
1// Left View: pick first element of each level
2for (auto& level : levels)
3    left.push_back(level[0]);     // first = leftmost
4
5// Right View: pick last element of each level
6for (auto& level : levels)
7    right.push_back(level.back());  // last = rightmost
04

Optimal — the tree and the idea

Optimal · O(N) time · O(H) space

The optimal approach uses DFS (pre-order traversal) with a clever trick: track the current depth (level) as you recurse. When you encounter a level for the first time (res.size() == level), the current node is the first one visited at that depth — add it to the result. For the left view, recurse left first; for the right view, recurse right first. No queue, no 2D vector — just a single result vector and the recursion stack.

Optimal Tree (skewed right chain)
L0 L1 L2 L3 L4 1 2 3 4 5 6
leftDFS — the key idea
1void leftDFS(TreeNode* node,
2             int level, vector<int>& res) {
3    if (!node) return;
4
5    // First time reaching this level?
6    if (res.size() == level)
7        res.push_back(node->val);
8
9    // Left first → leftmost node
10   leftDFS(node->left, level+1, res);
11   leftDFS(node->right, level+1, res);
12}

For rightDFS, swap the recursion order:
right first → rightmost node

Expected: Left View = [1, 2, 4, 5, 6] · Right View = [1, 3, 4, 5, 6]

05

Optimal — Left DFS trace (left child first)

The DFS visits nodes in pre-order, always going left first. The condition res.size() == level is true only the first time a depth is reached — and because left is explored first, that first node is the leftmost one at that level. Once a level is "claimed", all other nodes at the same depth are skipped.

DFS order: 1@L0 2@L1 4@L2 5@L3 6@L4 3@L1 (skip)
Left view — highlighted nodes
L0 L1 L2 L3 L4 1 2 3 4 5 6
DFS trace — left view
1 visit node 1 level=0 size=0 ADD → res={1}
2 visit node 2 level=1 size=1 ADD → res={1,2}
3 visit node 4 level=2 size=2 ADD → res={1,2,4}
4 visit node 5 level=3 size=3 ADD → res={1,2,4,5}
5 visit node 6 level=4 size=4 ADD → res={1,2,4,5,6}
6 visit node 3 level=1 size=5 SKIP (5≠1)
Left View result
1 2 4 5 6
06

Optimal — Right DFS trace (right child first)

Same logic, but the recursion order is swapped: right child first, then left. This means the first node encountered at each depth is the rightmost one. Node 3 is visited at level 1 before node 2, so 3 claims that level. But node 2's right chain (4, 5, 6) descends through levels 2-4 that node 3 couldn't reach (it has no children), so 4, 5, 6 are all claimed on the way back.

DFS order: 1@L0 3@L1 2@L1 (skip) 4@L2 5@L3 6@L4
Right view — highlighted nodes
L0 L1 L2 L3 L4 1 2 3 4 5 6
DFS trace — right view
1 visit node 1 level=0 size=0 ADD → res={1}
2 visit node 3 level=1 size=1 ADD → res={1,3}
3 visit node 2 level=1 size=2 SKIP (2≠1)
4 visit node 4 level=2 size=2 ADD → res={1,3,4}
5 visit node 5 level=3 size=3 ADD → res={1,3,4,5}
6 visit node 6 level=4 size=4 ADD → res={1,3,4,5,6}
Right View result
1 3 4 5 6
07

Brute Force vs Optimal — side by side

Both approaches produce the same result, but the optimal one is more memory-efficient. The brute force stores the entire level-order traversal (a 2D vector with all N nodes) just to pick one node per level. The optimal approach uses only a single result vector and the recursion stack — no extra storage for intermediate data.

Aspect Brute Force (BFS) Optimal (DFS)
Technique BFS level-order, then pick level[0] / level.back() DFS pre-order with res.size() == level check
Data structures queue + 2D vector (all levels stored) Single result vector + recursion stack
Traversal order Level by level (breadth-first) Depth-first (left-first for left view, right-first for right view)
Key insight First/last element of each level = left/right view First node visited at a new depth = view node (order determines which view)
Time complexity O(N) — every node visited once O(N) — every node visited once
Space complexity O(N) — queue + 2D vector stores all nodes O(H) — only recursion stack (H = tree height)
Two passes? Yes — BFS then iterate to extract (but reuses same levels vector) No — single DFS pass builds the result directly
Best for When you also need the full level-order traversal for other purposes When you only need the view — minimal memory, clean code
The critical difference — recursion order
// Left View: recurse LEFT first → leftmost node claims each level
leftDFS(node->left,  level + 1, res);   // ← left first
leftDFS(node->right, level + 1, res);

// Right View: recurse RIGHT first → rightmost node claims each level
rightDFS(node->right, level + 1, res);  // ← right first
rightDFS(node->left,  level + 1, res);

Both approaches find the side views

The brute force approach is intuitive — build the level-order table, pick the edges. The optimal approach is elegant — a single DFS pass where the recursion order determines which side you see. The key insight is that res.size() == level acts as a "first visit" detector: it's true exactly once per depth, and the node that triggers it is the view node for that level.

Brute Force Left View: 1 2 4
Brute Force Right View: 1 3 5
Optimal Left View: 1 2 4 5 6
Optimal Right View: 1 3 4 5 6
O(N)
Time — Both
O(N)
Space — Brute
O(H)
Space — Optimal
1
Pass — Optimal
Brute Force
O(N) time · O(N) space
BFS queue holds up to N/2 nodes (widest level). The 2D vector stores all N nodes. Two separate loops for left/right extraction, but both reuse the same levels vector.
Optimal
O(N) time · O(H) space
Recursion stack depth = tree height H. In the worst case (skewed tree) H = N, but for a balanced tree H = log N. The result vector has at most H entries (one per level). No intermediate storage needed.

Bottom Ad

Top Ad

Advertisement