Facebook Pixel

883. Projection Area of 3D Shapes

EasyGeometryArrayMathMatrix
Leetcode Link

Problem Description

You have an n x n grid where each cell contains a value representing the height of a tower of unit cubes stacked at that position. Specifically, grid[i][j] tells you how many 1 x 1 x 1 cubes are stacked vertically at position (i, j).

Your task is to calculate the total area of all three orthogonal projections (shadows) of this 3D structure:

  1. Top view (xy plane): Looking down from above, you see a shadow on the xy plane. Any position with at least one cube (value > 0) contributes 1 unit of area to this projection.

  2. Front view (yz plane): Looking from the front along the x-axis, you see a shadow on the yz plane. For each row, the tallest tower in that row determines the height of the shadow. The total area is the sum of maximum values from each row.

  3. Side view (zx plane): Looking from the side along the y-axis, you see a shadow on the zx plane. For each column, the tallest tower in that column determines the height of the shadow. The total area is the sum of maximum values from each column.

Return the sum of all three projection areas.

For example, if you have a grid [[1,2],[3,4]]:

  • Top view area: 4 (all positions have cubes)
  • Front view area: max(1,2) + max(3,4) = 2 + 4 = 6
  • Side view area: max(1,3) + max(2,4) = 3 + 4 = 7
  • Total: 4 + 6 + 7 = 17
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

To understand how to solve this problem, let's think about what each projection actually represents:

When we look at a 3D structure from different angles, we're essentially "flattening" it onto a 2D plane. The key insight is that each projection captures different information about the structure:

  1. Top view (xy plane): When looking down from above, we can't see the height of the cubes - we can only see whether a position is occupied or not. It's like looking at the footprint of a building from a satellite view. Each occupied cell contributes exactly 1 unit of area, regardless of how tall the tower is. So we just need to count how many cells have grid[i][j] > 0.

  2. Front view (yz plane): When looking from the front, we see the silhouette of each row. The tallest tower in each row determines the height of that row's shadow. Think of it like looking at a city skyline from the front - for each row of buildings, only the tallest one contributes to the skyline. Therefore, we sum up max(row) for each row.

  3. Side view (zx plane): Similarly, when looking from the side, we see the silhouette of each column. The tallest tower in each column determines the height of that column's shadow. We sum up max(column) for each column.

The beauty of this approach is that we can calculate each projection independently and then simply add them together. We don't need to actually construct a 3D model - we just need to extract the relevant information from the 2D grid that represents the heights.

This leads us to the solution: iterate through the grid once to count non-zero values for the xy projection, find the maximum of each row for the yz projection, and find the maximum of each column for the zx projection.

Learn more about Math patterns.

Solution Approach

The implementation follows directly from our understanding of the three projections:

1. Calculate xy plane projection (top view):

xy = sum(v > 0 for row in grid for v in row)

We iterate through every cell in the grid using a nested generator expression. For each value v, we check if it's greater than 0 (indicating at least one cube exists at that position). The sum() function counts how many True values we have, giving us the total footprint area.

2. Calculate yz plane projection (front view):

yz = sum(max(row) for row in grid)

For each row in the grid, we find the maximum value using max(row), which represents the tallest tower in that row. We sum all these maximum values to get the total area of the front projection.

3. Calculate zx plane projection (side view):

zx = sum(max(col) for col in zip(*grid))

This is the trickiest part. We use zip(*grid) to transpose the grid, effectively converting rows into columns. The * operator unpacks the grid rows, and zip() groups elements by their column index. For example, if grid = [[1,2],[3,4]], then zip(*grid) produces [(1,3), (2,4)], where each tuple represents a column. We then find the maximum value in each column and sum them up.

4. Return the total area:

return xy + yz + zx

Simply add all three projection areas together.

The time complexity is O(n²) where n is the grid dimension, as we need to examine each cell at least once. The space complexity is O(1) if we don't count the space used by the generator expressions and zip operation, or O(n) if we consider the temporary column structures created by zip(*grid).

Ready to land your dream job?

Unlock your dream job with a 3-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a concrete example with the grid [[2,1,0],[1,0,3],[0,2,1]]:

Grid visualization (heights):
2  1  0
1  0  3
0  2  1

Step 1: Calculate xy plane projection (top view)

Looking down from above, we check which positions have cubes (value > 0):

  • Position (0,0): 2 > 0 ✓ contributes 1
  • Position (0,1): 1 > 0 ✓ contributes 1
  • Position (0,2): 0 = 0 ✗ contributes 0
  • Position (1,0): 1 > 0 ✓ contributes 1
  • Position (1,1): 0 = 0 ✗ contributes 0
  • Position (1,2): 3 > 0 ✓ contributes 1
  • Position (2,0): 0 = 0 ✗ contributes 0
  • Position (2,1): 2 > 0 ✓ contributes 1
  • Position (2,2): 1 > 0 ✓ contributes 1

Total xy projection = 6 (six positions have cubes)

Step 2: Calculate yz plane projection (front view)

Looking from the front, we find the maximum height in each row:

  • Row 0: [2, 1, 0] → max = 2
  • Row 1: [1, 0, 3] → max = 3
  • Row 2: [0, 2, 1] → max = 2

Total yz projection = 2 + 3 + 2 = 7

Step 3: Calculate zx plane projection (side view)

Looking from the side, we find the maximum height in each column:

  • Column 0: [2, 1, 0] → max = 2
  • Column 1: [1, 0, 2] → max = 2
  • Column 2: [0, 3, 1] → max = 3

Total zx projection = 2 + 2 + 3 = 7

Step 4: Sum all projections

Total area = xy + yz + zx = 6 + 7 + 7 = 20

The answer is 20.

Solution Implementation

1class Solution:
2    def projectionArea(self, grid: List[List[int]]) -> int:
3        # Calculate the projection area on XY plane (top view)
4        # Count all non-zero cells (each non-zero value creates a shadow of area 1)
5        xy_projection = sum(value > 0 for row in grid for value in row)
6      
7        # Calculate the projection area on YZ plane (front view)
8        # For each row, take the maximum height (looking from the side)
9        yz_projection = sum(max(row) for row in grid)
10      
11        # Calculate the projection area on ZX plane (side view)
12        # For each column, take the maximum height (looking from the front)
13        # zip(*grid) transposes the grid to iterate through columns
14        zx_projection = sum(max(column) for column in zip(*grid))
15      
16        # Return the total projection area (sum of all three projections)
17        return xy_projection + yz_projection + zx_projection
18
1class Solution {
2    public int projectionArea(int[][] grid) {
3        // Initialize projection areas for three different views
4        int topViewArea = 0;      // xy-plane projection (looking from above)
5        int frontViewArea = 0;     // yz-plane projection (looking from front)
6        int sideViewArea = 0;      // zx-plane projection (looking from side)
7      
8        int n = grid.length;
9      
10        // Iterate through each row
11        for (int i = 0; i < n; i++) {
12            int maxHeightInRow = 0;     // Maximum height in current row (for front view)
13            int maxHeightInColumn = 0;  // Maximum height in current column (for side view)
14          
15            // Iterate through each column
16            for (int j = 0; j < n; j++) {
17                // Count non-zero cells for top view projection
18                if (grid[i][j] > 0) {
19                    topViewArea++;
20                }
21              
22                // Track maximum height in current row (grid[i][j])
23                maxHeightInRow = Math.max(maxHeightInRow, grid[i][j]);
24              
25                // Track maximum height in current column (grid[j][i])
26                maxHeightInColumn = Math.max(maxHeightInColumn, grid[j][i]);
27            }
28          
29            // Add the maximum heights to respective projection areas
30            frontViewArea += maxHeightInRow;
31            sideViewArea += maxHeightInColumn;
32        }
33      
34        // Return the sum of all three projection areas
35        return topViewArea + frontViewArea + sideViewArea;
36    }
37}
38
1class Solution {
2public:
3    int projectionArea(vector<vector<int>>& grid) {
4        // Initialize projection areas for each plane
5        int projectionXY = 0;  // Top-down view (xy-plane)
6        int projectionYZ = 0;  // Side view from x-axis (yz-plane)
7        int projectionZX = 0;  // Front view from y-axis (zx-plane)
8      
9        int n = grid.size();
10      
11        // Iterate through each row
12        for (int i = 0; i < n; ++i) {
13            int maxHeightInRow = 0;     // Maximum height in current row (for yz-plane)
14            int maxHeightInColumn = 0;  // Maximum height in current column (for zx-plane)
15          
16            // Iterate through each column
17            for (int j = 0; j < n; ++j) {
18                // Count non-zero cells for xy-plane projection (top view)
19                if (grid[i][j] > 0) {
20                    projectionXY++;
21                }
22              
23                // Track maximum height in current row for yz-plane projection
24                maxHeightInRow = max(maxHeightInRow, grid[i][j]);
25              
26                // Track maximum height in current column for zx-plane projection
27                // Note: grid[j][i] accesses column i by iterating through rows
28                maxHeightInColumn = max(maxHeightInColumn, grid[j][i]);
29            }
30          
31            // Add the maximum heights to respective projections
32            projectionYZ += maxHeightInRow;
33            projectionZX += maxHeightInColumn;
34        }
35      
36        // Return the sum of all three projection areas
37        return projectionXY + projectionYZ + projectionZX;
38    }
39};
40
1/**
2 * Calculates the total projection area of 3D shapes on three orthogonal planes
3 * @param grid - 2D array where grid[i][j] represents the height of a tower at position (i, j)
4 * @returns The sum of projection areas on xy, yz, and zx planes
5 */
6function projectionArea(grid: number[][]): number {
7    // Calculate xy plane projection (top view)
8    // Count all positions with height > 0
9    const xyPlaneArea: number = grid
10        .flat()
11        .filter((height: number) => height > 0)
12        .length;
13  
14    // Calculate yz plane projection (front view)
15    // Sum of maximum heights in each row
16    const yzPlaneArea: number = grid.reduce(
17        (totalArea: number, currentRow: number[]) => totalArea + Math.max(...currentRow), 
18        0
19    );
20  
21    // Calculate zx plane projection (side view)
22    // Sum of maximum heights in each column
23    const zxPlaneArea: number = grid[0]
24        .map((_: number, columnIndex: number) => 
25            Math.max(...grid.map((row: number[]) => row[columnIndex]))
26        )
27        .reduce(
28            (totalArea: number, maxHeight: number) => totalArea + maxHeight, 
29            0
30        );
31  
32    // Return the total projection area across all three planes
33    return xyPlaneArea + yzPlaneArea + zxPlaneArea;
34}
35

Time and Space Complexity

The time complexity is O(n²), where n is the side length of the grid. This is because:

  • The xy calculation iterates through all elements in the grid once: O(n²)
  • The yz calculation finds the max of each row for n rows, with each max operation taking O(n) time: O(n²)
  • The zx calculation uses zip(*grid) to transpose the grid (which takes O(n²) time) and then finds the max of each column for n columns: O(n²)
  • Overall: O(n²) + O(n²) + O(n²) = O(n²)

The space complexity is O(n). While the reference answer states O(1), the use of zip(*grid) creates an iterator that generates tuples representing the columns of the grid. In Python 3, zip returns an iterator rather than a list, but when used with max(col) for col in zip(*grid), each column tuple must be materialized, requiring O(n) space for each column as it's processed. However, only one column is held in memory at a time during the generator expression evaluation, resulting in O(n) space complexity.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Misunderstanding the Top View Projection

Pitfall: A common mistake is calculating the top view projection as sum(sum(row) for row in grid), which would sum all the values in the grid. This is incorrect because the top view only cares about whether a position has cubes or not, not how many cubes are stacked there.

Wrong approach:

xy_projection = sum(sum(row) for row in grid)  # WRONG: sums all heights

Correct approach:

xy_projection = sum(value > 0 for row in grid for value in row)  # Counts positions with cubes

2. Confusing Row vs Column Projections

Pitfall: Mixing up which projection corresponds to rows and which to columns. Remember:

  • Front view (yz plane) looks along the x-axis → sees one profile per row → use row maximums
  • Side view (zx plane) looks along the y-axis → sees one profile per column → use column maximums

Wrong approach:

yz_projection = sum(max(column) for column in zip(*grid))  # WRONG: using columns for yz
zx_projection = sum(max(row) for row in grid)  # WRONG: using rows for zx

Correct approach:

yz_projection = sum(max(row) for row in grid)  # Rows for front view
zx_projection = sum(max(column) for column in zip(*grid))  # Columns for side view

3. Handling Empty Rows or Columns

Pitfall: If the grid could contain empty rows (all zeros), calling max() on an empty sequence would raise a ValueError. While the problem typically guarantees non-empty grids, it's good practice to handle edge cases.

Safer approach:

yz_projection = sum(max(row) if row else 0 for row in grid)
zx_projection = sum(max(column) if column else 0 for column in zip(*grid))

4. Not Understanding the zip(*grid) Transpose

Pitfall: Some developers try to manually iterate through columns using nested loops, which is more error-prone and less readable.

Inefficient approach:

zx_projection = 0
for j in range(len(grid[0])):
    max_in_column = 0
    for i in range(len(grid)):
        max_in_column = max(max_in_column, grid[i][j])
    zx_projection += max_in_column

Better approach:

zx_projection = sum(max(column) for column in zip(*grid))

The zip(*grid) idiom is a Pythonic way to transpose a 2D list and makes the code cleaner and more maintainable.

Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

Consider the classic dynamic programming of longest increasing subsequence:

Find the length of the longest subsequence of a given sequence such that all elements of the subsequence are sorted in increasing order.

For example, the length of LIS for [50, 3, 10, 7, 40, 80] is 4 and LIS is [3, 7, 40, 80].

What is the recurrence relation?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More