Skip to content
← All posts

Projection Area of 3D Shapes: Count What You Can See

5 min read
leetcodeproblemeasyarraysmathmatrix

You are given an n x n grid where each cell grid[i][j] represents the height of a stack of unit cubes sitting on position (i, j). Imagine looking at this 3D shape from three directions: straight down from above, from the front, and from the side. Each viewpoint produces a 2D shadow (a projection). Your job is to compute the total area of all three projections combined.

This is LeetCode 883: Projection Area of 3D Shapes, and despite being tagged "easy," it is a surprisingly satisfying exercise in thinking about 2D representations of 3D data. The solution is short, but the insight behind it is worth understanding deeply.

Grid: [[1, 2], [3, 4]]1234col 0col 1row 0row 1Top view: count non-zero = 41111Front view: max per column = [3, 4]34Side view: max per row = [2, 4]24Total = 4 + 7 + 6 = 17
The three projections of grid [[1, 2], [3, 4]]. Top = 4 non-zero cells, front = sum of column maxes (3+4=7), side = sum of row maxes (2+4=6). Total projection area = 17.

Why this problem matters

At first glance, this feels like a geometry puzzle, but it is really a matrix traversal problem. You are not doing any 3D math. You are scanning a 2D grid and computing three different aggregate values: a count, column maximums, and row maximums. That is it.

This makes it a great warm-up for harder matrix problems. It trains you to think about a grid from multiple perspectives and to extract different statistics in a single pass. The same skill shows up in problems like Valid Sudoku, where you need to track constraints per row, per column, and per sub-box simultaneously.

It also reinforces a pattern that appears constantly in interview problems: reducing a multi-dimensional problem to simple aggregations over rows and columns. Once you see that the "3D projection" is just "row max" and "column max," the problem dissolves.

Breaking down the three views

Each projection captures a different cross-section of the shape:

Top view (looking straight down): you see a shadow of every position that has at least one cube. If grid[i][j] > 0, that cell contributes 1 unit of area. If it is 0, there is nothing there, so it contributes 0. The top projection area is simply the count of non-zero cells.

Front view (looking from the front along the rows): for each column, you see the tallest stack in that column. Shorter stacks are hidden behind the tallest one. So the front projection for column j is max(grid[0][j], grid[1][j], ..., grid[n-1][j]), the maximum value in that column. Sum these column maxes to get the front area.

Side view (looking from the side along the columns): for each row, you see the tallest stack in that row. The side projection for row i is max(grid[i][0], grid[i][1], ..., grid[i][n-1]), the maximum value in that row. Sum these row maxes to get the side area.

The total projection area is the sum of all three.

Step 1: Start with the grid

col 0col 1row 0row 12013

Grid = [[2, 0], [1, 3]]. We compute three separate projections and add them up.

Step 2: Top projection (looking down from above)

2013non-zero?2: yes0: no1: yes3: yes= 3

Count cells where grid[r][c] > 0. Cells with value 2, 1, and 3 are non-zero. The cell with 0 is empty. Top area = 3.

Step 3: Front projection (looking from the front)

2013col 0col 1vv23= 5max per colcol 0: max(2,1)=2col 1: max(0,3)=3

Take the maximum value in each column. Column 0: max(2, 1) = 2. Column 1: max(0, 3) = 3. Front area = 2 + 3 = 5.

Step 4: Side projection (looking from the side)

2013row 0row 1>>23max per rowrow 0: max(2,0)=2row 1: max(1,3)=3= 5

Take the maximum value in each row. Row 0: max(2, 0) = 2. Row 1: max(1, 3) = 3. Side area = 2 + 3 = 5.

Step 5: Add all three projections

Top: 3+Front: 5+Side: 5= 13

Total projection area = top (3) + front (5) + side (5) = 13.

Clean solution

You can compute all three projections in a single pass through the grid. Walk through each row, track the row maximum for the side view, and update running column maximums for the front view. Count non-zero cells for the top view as you go.

def projection_area(grid):
    n = len(grid)
    top = 0
    front = [0] * n
    side = 0
    total = 0

    for i in range(n):
        row_max = 0
        for j in range(n):
            if grid[i][j] > 0:
                top += 1
            front[j] = max(front[j], grid[i][j])
            row_max = max(row_max, grid[i][j])
        total += row_max

    total += top + sum(front)
    return total

This visits each cell exactly once and computes all three projections along the way. The front array accumulates column maximums across rows. The row_max variable resets for each row, giving us the side projection. And the top counter just counts non-zero entries.

Complexity analysis

ApproachTimeSpace
Single passO(n^2)O(n)

Time: O(n^2) because you visit every cell in the n x n grid exactly once.

Space: O(n) for the front array that stores column maximums. You could also precompute everything with Python's zip and built-in max, but the single-pass approach is cleaner and uses less extra space.

Edge cases to watch for

  • All zeros: every cell is 0. The grid is empty, so all three projections are 0. Total = 0.
  • Single cell: a 1x1 grid with value v. Top = 1 (if v > 0), front = v, side = v. Total = 1 + 2v (or 0 if v is 0).
  • One non-zero cell: e.g., a 3x3 grid where only grid[1][2] = 5. Top = 1, front = 5 (column 2 max), side = 5 (row 1 max). All other column and row maxes are 0.
  • Uniform grid: every cell has the same value k. Top = nn, front = nk, side = n*k. Total = n^2 + 2nk.
  • Grid with zeros scattered: zeros only affect the top projection (they reduce the count). They do not affect front or side projections unless an entire row or column is zero.

The building blocks

This problem is built on a simple but reusable idea: row and column aggregation over a matrix. You iterate through a 2D grid and compute aggregate statistics (max, sum, count) along rows and columns independently.

This same pattern appears in many other problems:

  • Set Matrix Zeroes (LeetCode 73): scan the matrix to find which rows and columns contain zeros, then use that row/column information to zero out cells. Same idea of reducing a 2D problem to row and column properties.
  • Valid Sudoku (LeetCode 36): check constraints per row, per column, and per box. You are extracting multiple aggregate views of the same 2D data.
  • Search a 2D Matrix (LeetCode 74): leverage the sorted structure of rows and columns to locate an element. Understanding how rows and columns relate to the overall structure is the key insight.

Once you recognize that "projection" is just another word for "aggregate along an axis," this problem becomes a one-pass scan. That shift in thinking, from geometry to aggregation, is the real takeaway.

From understanding to recall

The solution here is short, but the decomposition is what matters. Breaking a 3D projection into three independent matrix aggregations is the kind of insight that transfers to harder problems. When you see a problem that asks for "views" or "shadows" of a data structure, you now know to ask: "What aggregate does each view correspond to?"

Spaced repetition helps you internalize this decomposition. You practice identifying the three projections, writing the single-pass solution, and recalling which axis each view corresponds to. After a few reps, you do not need to re-derive the approach. You see "projection area" and immediately think: count non-zero, column max, row max. Done.

Row and column aggregation is one of roughly 60 reusable building blocks that cover hundreds of LeetCode problems. Drilling each one individually with spaced repetition is far more effective than grinding random problems and hoping the patterns stick.

Related posts

  • Spiral Matrix - Another matrix traversal problem where thinking about the grid from multiple directions is the key insight
  • Set Matrix Zeroes - Uses row and column markers to transform a 2D grid, the same "reduce to row/column properties" idea
  • Valid Sudoku - Validates constraints per row, per column, and per sub-box, another problem where you extract multiple aggregate views from a single grid