Facebook Pixel

119. Pascal's Triangle II

Problem Description

Given an integer rowIndex, you need to return the row at index rowIndex in Pascal's triangle. The indexing is 0-based, meaning the first row is at index 0.

In Pascal's triangle, each number is calculated as the sum of the two numbers directly above it. The triangle starts with 1 at the top, and each row begins and ends with 1.

For example:

  • Row 0: [1]
  • Row 1: [1, 1]
  • Row 2: [1, 2, 1]
  • Row 3: [1, 3, 3, 1]
  • Row 4: [1, 4, 6, 4, 1]

Each interior number is formed by adding the two numbers from the previous row that are positioned diagonally above it. For instance, in row 3, the number 3 comes from adding 1 + 2 from row 2.

The task is to return just the specified row as a list of integers, without generating the entire triangle.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is that we don't need to generate all previous rows to get the desired row. Instead, we can build the target row directly by modifying values in-place.

Consider how Pascal's triangle works: each element is the sum of the two elements above it. If we look at any row, we can generate the next row by:

  • Starting with 1 at both ends
  • Each middle element = left parent + right parent

But here's the clever part: we can reuse the same array and update it row by row. Starting with an array of all 1s (which represents having 1s at the boundaries), we can transform it into each successive row.

The critical observation is that when updating the array to represent the next row, we must work backwards (from right to left). Why? Because each position needs the original values from the current row to calculate the new value. If we went left to right, we'd overwrite values we still need.

For example, transforming [1, 2, 1] to [1, 3, 3, 1]:

  • Position 2: f[2] = f[2] + f[1] = 1 + 2 = 3
  • Position 1: f[1] = f[1] + f[0] = 2 + 1 = 3

By going backwards, when we update position 2, we haven't touched position 1 yet, so it still holds the value we need. This way, we only need O(n) space instead of O(n²) for storing all rows, and we avoid the overhead of creating new arrays for each row.

Solution Approach

We implement the solution using dynamic programming with space optimization. Here's the step-by-step approach:

  1. Initialize the array: Create an array f of size rowIndex + 1 with all elements set to 1. This represents the initial state where all positions have value 1, which correctly handles the boundary elements that are always 1 in Pascal's triangle.

  2. Iterate through rows: Starting from row 2 (since rows 0 and 1 are already correct with all 1s), we iterate up to rowIndex. For each row i, we need to update the middle elements.

  3. Update elements backwards: For row i, we update positions from i-1 down to 1 (working right to left). The update formula is:

    f[j] = f[j] + f[j-1]

    This adds the current value at position j with the value to its left, which gives us the correct Pascal's triangle value for the next row.

  4. Why backwards iteration works: When we update position j, we need the original values at positions j and j-1 from the current row. By iterating backwards:

    • Position j hasn't been modified yet, so it still holds its original value
    • Position j-1 also hasn't been modified (since we're going backwards)
    • After updating, position j now contains the correct value for the new row
  5. Return the result: After completing all iterations, array f contains the values of row rowIndex.

The time complexity is O(rowIndex²) since we iterate through each row and update multiple elements per row. The space complexity is O(rowIndex) as we only maintain a single array of size rowIndex + 1.

Ready to land your dream job?

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

Start Evaluator

Example Walkthrough

Let's walk through finding row 3 of Pascal's triangle (which should be [1, 3, 3, 1]).

Step 1: Initialize array We create an array of size 4 (rowIndex + 1) filled with 1s:

f = [1, 1, 1, 1]

This already represents row 0 and row 1 correctly.

Step 2: Build row 2 (i = 2) We need to update positions from index 1 to 1 (just one position):

  • Position 1: f[1] = f[1] + f[0] = 1 + 1 = 2

After row 2:

f = [1, 2, 1, 1]

Note: Only the first 3 elements matter for row 2, giving us [1, 2, 1].

Step 3: Build row 3 (i = 3) We update positions from index 2 down to 1 (working backwards):

  • Position 2: f[2] = f[2] + f[1] = 1 + 2 = 3
  • Position 1: f[1] = f[1] + f[0] = 2 + 1 = 3

After row 3:

f = [1, 3, 3, 1]

Why backwards iteration is crucial: When updating position 2 in Step 3, we need the original value at position 1 (which is 2). If we went forward:

  • Updating position 1 first would give us f[1] = 2 + 1 = 3
  • Then position 2 would incorrectly become f[2] = 1 + 3 = 4

By going backwards, position 1 retains its value of 2 when we calculate position 2, ensuring the correct result.

Final Result: [1, 3, 3, 1]

Solution Implementation

1from typing import List
2
3class Solution:
4    def getRow(self, rowIndex: int) -> List[int]:
5        """
6        Generate the rowIndex-th row of Pascal's Triangle.
7      
8        Args:
9            rowIndex: The index of the row to return (0-indexed)
10          
11        Returns:
12            List containing the values of the specified row in Pascal's Triangle
13        """
14        # Initialize the row with all 1s
15        # Size is rowIndex + 1 because row n has n + 1 elements
16        row = [1] * (rowIndex + 1)
17      
18        # Build up the row iteratively from row 2 to rowIndex
19        # Row 0 is [1], Row 1 is [1, 1], so we start from Row 2
20        for current_row in range(2, rowIndex + 1):
21            # Update values from right to left to avoid overwriting needed values
22            # Start from the second-to-last position and move towards index 1
23            for position in range(current_row - 1, 0, -1):
24                # Each element is the sum of the two elements above it
25                # in the previous row (which are at positions j-1 and j)
26                row[position] += row[position - 1]
27      
28        return row
29
1class Solution {
2    /**
3     * Returns the row at the given index in Pascal's Triangle.
4     * Uses space-optimized approach with O(rowIndex) space complexity.
5     * 
6     * @param rowIndex The 0-indexed row number to return
7     * @return List containing the values of the specified row in Pascal's Triangle
8     */
9    public List<Integer> getRow(int rowIndex) {
10        // Initialize result list with capacity of rowIndex + 1
11        List<Integer> row = new ArrayList<>();
12      
13        // Pre-fill the list with 1s for the required size
14        // This sets up the base case where all elements start as 1
15        for (int i = 0; i <= rowIndex; i++) {
16            row.add(1);
17        }
18      
19        // Build up the row iteratively from row 2 to rowIndex
20        // Row 0: [1], Row 1: [1,1] are already correct
21        for (int currentRow = 2; currentRow <= rowIndex; currentRow++) {
22            // Update elements from right to left to avoid overwriting needed values
23            // Start from the second-to-last element and work backwards to index 1
24            // The first and last elements remain 1
25            for (int position = currentRow - 1; position > 0; position--) {
26                // Each element is the sum of itself and the element to its left
27                // This simulates Pascal's Triangle formula: C(n,k) = C(n-1,k-1) + C(n-1,k)
28                row.set(position, row.get(position) + row.get(position - 1));
29            }
30        }
31      
32        return row;
33    }
34}
35
1class Solution {
2public:
3    vector<int> getRow(int rowIndex) {
4        // Initialize result vector with all 1s
5        // Size is rowIndex + 1 since row indices start from 0
6        vector<int> row(rowIndex + 1, 1);
7      
8        // Build Pascal's triangle row by row using dynamic programming
9        // Start from row 2 (index 2) since rows 0 and 1 are already correct
10        for (int currentRow = 2; currentRow <= rowIndex; ++currentRow) {
11            // Update values from right to left to avoid overwriting needed values
12            // Start from second-to-last element and work backwards
13            // Skip first and last elements as they remain 1
14            for (int col = currentRow - 1; col > 0; --col) {
15                // Each element is the sum of the two elements above it
16                // row[col] = row[col] + row[col - 1] from previous row
17                row[col] += row[col - 1];
18            }
19        }
20      
21        return row;
22    }
23};
24
1/**
2 * Generates the specified row of Pascal's Triangle
3 * @param rowIndex - The index of the row to generate (0-indexed)
4 * @returns An array representing the rowIndex-th row of Pascal's Triangle
5 */
6function getRow(rowIndex: number): number[] {
7    // Initialize array with all 1s for the row
8    // Length is rowIndex + 1 since row 0 has 1 element, row 1 has 2 elements, etc.
9    const pascalRow: number[] = Array(rowIndex + 1).fill(1);
10  
11    // Build the row iteratively using the property:
12    // Each element is the sum of the two elements above it in the previous row
13    for (let currentRow = 2; currentRow <= rowIndex; currentRow++) {
14        // Update elements from right to left to avoid overwriting needed values
15        // Start from the second-to-last element of the current row
16        for (let position = currentRow - 1; position > 0; position--) {
17            // Each element equals itself plus the element to its left
18            pascalRow[position] += pascalRow[position - 1];
19        }
20    }
21  
22    return pascalRow;
23}
24

Time and Space Complexity

The time complexity is O(n^2), where n = rowIndex. This is because we have two nested loops:

  • The outer loop runs from i = 2 to rowIndex, which is approximately n - 1 iterations
  • The inner loop runs from j = i - 1 to 1, which is approximately i - 1 iterations for each i
  • The total number of operations is approximately 1 + 2 + 3 + ... + (n-1) = (n-1) * n / 2, which is O(n^2)

The space complexity is O(n), where n = rowIndex + 1. This is because:

  • We create a single array f of size rowIndex + 1 to store the result
  • We modify this array in-place without using any additional data structures that scale with the input
  • Therefore, the space used is linear with respect to the row index

Common Pitfalls

1. Iterating in the Wrong Direction (Left to Right)

One of the most common mistakes is updating the array elements from left to right instead of right to left. This causes incorrect values because we overwrite data that we still need.

Incorrect Implementation:

# WRONG: Iterating from left to right
for current_row in range(2, rowIndex + 1):
    for position in range(1, current_row):  # Going left to right
        row[position] += row[position - 1]

Why it fails: When we update row[1], we change it from its original value. Then when we try to update row[2], we need the original value of row[1], but it's already been modified, leading to incorrect calculations.

Example of the problem:

  • Starting with row [1, 1, 1] for generating row 2
  • Update position 1: row[1] = row[1] + row[0] = 1 + 1 = 2[1, 2, 1]
  • Update position 2: row[2] = row[2] + row[1] = 1 + 2 = 3[1, 2, 3]
  • Expected result should be [1, 2, 1]

Solution: Always iterate from right to left (backwards) to preserve the original values needed for calculations:

for position in range(current_row - 1, 0, -1):  # Right to left
    row[position] += row[position - 1]

2. Incorrect Array Size Initialization

Another pitfall is initializing the array with the wrong size. Remember that row n in Pascal's triangle has n + 1 elements.

Incorrect Implementation:

# WRONG: Using rowIndex instead of rowIndex + 1
row = [1] * rowIndex  # Missing one element!

Why it fails: This will cause an index out of bounds error or produce an incomplete row. For example, if rowIndex = 3, we need 4 elements [1, 3, 3, 1], not 3.

Solution: Always initialize with size rowIndex + 1:

row = [1] * (rowIndex + 1)

3. Off-by-One Errors in Loop Bounds

Getting the loop boundaries wrong is a frequent issue, especially for the inner loop.

Incorrect Implementation:

# WRONG: Including position 0 or going to current_row
for position in range(current_row, -1, -1):  # Including edges
    row[position] += row[position - 1]

Why it fails:

  • Position 0 should always remain 1 (edge of triangle)
  • Position current_row is the last element and should also remain 1
  • Trying to access row[-1] when position = 0 causes an error or unexpected behavior

Solution: The inner loop should go from current_row - 1 down to 1:

for position in range(current_row - 1, 0, -1):
    row[position] += row[position - 1]
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of the following array represent a max heap?


Recommended Readings

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

Load More