Surface Area of 3D Shapes: Grid Geometry
LeetCode 892: Surface Area of 3D Shapes gives you an n x n grid where grid[i][j] represents the number of unit cubes stacked at position (i, j). These stacks sit on a flat surface, forming a 3D shape. Your job is to compute the total exposed surface area of the entire shape.
At first, it feels like you need to think in three dimensions, tracking which faces are visible from every angle. But the real trick is to work cell by cell and think about overlaps between neighbors. Once you see the pattern, the solution is a single pass through the grid with some simple arithmetic.
Counting faces: the core idea
Consider a single column of v cubes sitting in isolation. How many faces does it expose?
- Top and bottom: exactly 1 face each, regardless of height. That gives you 2 faces.
- Four sides: each side of the column is
vunit squares tall. That gives you4 * vfaces.
So a single column of height v contributes 4 * v + 2 faces when it stands alone.
Now think about what happens when two adjacent columns touch. If column A has height h1 and column B has height h2, the shorter column presses against the taller one. The overlapping area on each side equals min(h1, h2) faces. Since both columns lose those faces (one on each side of the shared wall), the total faces removed is 2 * min(h1, h2).
The full formula becomes:
surface_area = sum(4 * v + 2 for each cell with v > 0)
- sum(2 * min(a, b) for each pair of adjacent cells)
You only need to check each pair once. Scanning right and down from every cell covers all adjacent pairs without double counting.
The solution
def surfaceArea(grid: list[list[int]]) -> int:
n = len(grid)
area = 0
for i in range(n):
for j in range(n):
if grid[i][j] > 0:
area += 4 * grid[i][j] + 2
if i + 1 < n:
area -= 2 * min(grid[i][j], grid[i + 1][j])
if j + 1 < n:
area -= 2 * min(grid[i][j], grid[i][j + 1])
return area
For each cell, you add its isolated contribution (if the height is positive), then subtract the overlap with the neighbor to the right and the neighbor below. That is the entire algorithm.
Step-by-step walkthrough
Step 1: Base contribution for each cell
For each cell with height v > 0, start with 4v + 2 faces: v faces on each of 4 sides, plus top and bottom. Sum of all base contributions = 6 + 10 + 14 + 18 = 48.
Step 2: Cell (0,0), v=1, check right and down neighbors
Right neighbor (0,1) has height 2. Overlap = 2*min(1,2) = 2. Down neighbor (1,0) has height 3. Overlap = 2*min(1,3) = 2. Total overlap for this cell = 4.
Step 3: Cell (0,1), v=2, check right and down neighbors
No right neighbor (edge of grid). Down neighbor (1,1) has height 4. Overlap = 2*min(2,4) = 4. Total overlap for this cell = 4.
Step 4: Cell (1,0), v=3, check right and down neighbors
Right neighbor (1,1) has height 4. Overlap = 2*min(3,4) = 6. No down neighbor (edge of grid). Total overlap for this cell = 6.
Step 5: Cell (1,1), v=4, check right and down neighbors
No right neighbor, no down neighbor (corner of grid). Nothing to subtract for this cell.
Step 6: Final calculation
Base contributions: 6 + 10 + 14 + 18 = 48. Total overlap: 4 + 4 + 6 + 0 = 14. Surface area = 48 - 14 = 34.
Complexity analysis
| Approach | Time | Space |
|---|---|---|
| Grid scan | O(n^2) | O(1) |
Time: You visit every cell once and do constant work per cell (at most two neighbor comparisons). That is O(n^2) for an n x n grid.
Space: You use a single integer accumulator. No extra arrays, no recursion. That is O(1).
Building blocks
1. Grid neighbor traversal
The core mechanic here is scanning each cell and comparing it against its right and down neighbors. This "only check right and down" trick avoids double counting: every pair of adjacent cells gets processed exactly once. You will see this same traversal pattern in Island Perimeter, Number of Islands, and many other grid problems.
for i in range(n):
for j in range(n):
if j + 1 < n:
# process (i,j) and (i,j+1) as a horizontal pair
if i + 1 < n:
# process (i,j) and (i+1,j) as a vertical pair
2. Overlap subtraction
When two values share a boundary, the hidden area equals the minimum of the two. This min(a, b) pattern shows up whenever you need to count "how much is blocked." In this problem, min(h1, h2) tells you how many unit squares of wall are hidden on each side. Multiplying by 2 accounts for both touching surfaces. The same idea appears in problems involving water trapped between bars, overlapping intervals, and rectangle intersection.
Edge cases
- Single cell: a 1x1 grid with value
v. No neighbors, so the surface area is simply4v + 2. Ifvis 0, the area is 0. - All zeros: every cell is 0. No cubes exist, so the surface area is 0. The
v > 0check ensures you do not add the top and bottom for empty cells. - Uniform height: every cell has the same value
k. Each cell contributes4k + 2, and each adjacent pair removes2k. For an n x n grid, that works out ton^2 * (4k + 2) - 2 * (2 * n * (n - 1)) * ktotal faces.
From understanding to recall
The key insight here fits in one sentence: each column contributes 4v + 2 faces, and each neighbor pair hides 2 * min(h1, h2) faces. If you can recall that formula, the code writes itself in under a minute.
What trips people up under pressure is forgetting to handle the v > 0 guard (empty cells should not contribute the top and bottom faces) or accidentally double-counting neighbor pairs by checking all four directions. Practice writing the solution from scratch: the nested loop, the base contribution, the two neighbor subtractions. After a few spaced reps, the "right and down only" pattern will be automatic.
Surface area counting and neighbor overlap subtraction are two 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.
Related posts
- Projection Area of 3D Shapes - Related 3D grid geometry problem
- Island Perimeter - Similar neighbor-overlap counting for 2D shapes
- Max Area of Island - Grid traversal with area computation