118. Pascal's Triangle¶
Question¶
Given an integer numRows, return the first numRows of Pascal's triangle. In Pascal's triangle, each number is the sum of the two numbers directly above it.
Solution¶
Pattern¶
1D Dynamic Programming (State Transition) Build the current row based purely on the values of the immediately preceding row.
How to Identify¶
- The problem explicitly describes a generation process where the current state depends on the previous state.
- The structure is a grid or triangle where elements at \((r, c)\) depend on \((r-1, c-1)\) and \((r-1, c)\).
Description¶
Step-by-step explanation:
- Validate input. If
numRowsis 0, return an empty list. - Initialize the master list
resto hold all the rows. - Iterate
ifrom \(0\) tonumRows - 1. This loop represents the current row index. - For each row
i, create a new listrow. - Iterate
jfrom \(0\) toi. This loop builds the elements of the current row. - Boundary Condition: If
j == 0(first element) orj == i(last element), the value is strictly \(1\). Add \(1\) to therow. - Internal Condition: For all other elements, fetch the previous row (
res.get(i - 1)). The current value is the sum of the element directly above-left (j - 1) and above-right (j). Add this sum to therow. - Once the inner loop finishes, add
rowtores. - Return
res.
The Intuition¶
Think of this as building a brick wall. You cannot place a brick in the middle of a row until the two bricks directly beneath it (or "above" it, visually, in this problem) are securely in place. Because the rules of the wall strictly dictate that a brick's value is the sum of its two supporting bricks, you don't need any complex formulas. You just need to keep the very last row of bricks you built in your memory, and use it as the foundation to build the current row. The edges of the wall are always straight lines of 1s.
Complexity¶
| Label | Worst | Average |
|---|---|---|
| Time Complexity | \(O(N^2)\) | \(O(N^2)\) |
| Space Complexity | \(O(1)\) | \(O(1)\) |
Note: \(N\) represents numRows.
Time Complexity¶
The outer loop runs \(N\) times. The inner loop runs \(1, 2, 3, \dots, N\) times. The total number of iterations is the sum of an arithmetic progression: \(\frac{N(N+1)}{2}\). This scales quadratically, making it strictly \(O(N^2)\).
Space Complexity¶
The returned 2D array requires \(O(N^2)\) space to store the \(\approx \frac{N^2}{2}\) integers. However, auxiliary space (memory used beyond what is required to construct the output) is \(O(1)\), because we only create variables to hold the current row being built before appending it to the result list.
Code¶
class Solution {
public List<List<Integer>> generate(int numRows) {
List<List<Integer>> res = new ArrayList<>();
if (numRows == 0) return res;
for (int i = 0; i < numRows; i++) {
List<Integer> row = new ArrayList<>();
for (int j = 0; j <= i; j++) {
// The first and last elements of every row are always 1
if (j == 0 || j == i) {
row.add(1);
} else {
// Other elements are the sum of the two elements directly above
List<Integer> prevRow = res.get(i - 1);
row.add(prevRow.get(j - 1) + prevRow.get(j));
}
}
res.add(row);
}
return res;
}
}
Caveats¶
- Integer Overflow: For standard LeetCode constraints (
numRowsup to 30), standard 32-bitintis fine. However, Pascal's Triangle values grow exponentially. By row 35, the middle elements will overflowInteger.MAX_VALUE. In a real system, you must switch tolongorBigIntegerfor \(N > 33\). - Formulaic Approach: Do not use the combinatorial formula \(nCr = \frac{n!}{r!(n-r)!}\) to generate the entire triangle. Calculating factorials for every single cell independently takes \(O(N^3)\) time and is highly prone to overflow, making it vastly inferior to the \(O(N^2)\) addition method.
Concepts to Think About¶
- Space Optimization: If the problem only asked you to return the last row (Pascal's Triangle II), you do not need to store the entire 2D triangle. You can compute the rows in-place using a single 1D array of size \(N\), updating it from back-to-front to achieve \(O(N)\) space.
- Binomial Expansion: The \(k\)-th row of Pascal's triangle represents the coefficients of the binomial expansion \((x+y)^k\).
- Combinatorics: The element at row \(n\) and column \(k\) (0-indexed) is exactly equal to \(\binom{n}{k}\) (n choose k).
Logical Follow-up¶
Question: Given an integer rowIndex, return only the \(k\)-th row of Pascal's triangle using strictly \(O(k)\) extra space. (LeetCode 119: Pascal's Triangle II)
Solution: We use a single 1D array/list initialized with 1s. We iterate \(k\) times. In each iteration, we traverse the array backwards from j = i - 1 down to 1, updating the array in place: row[j] = row[j] + row[j-1]. Traversing backwards ensures we don't overwrite the row[j-1] value before we need it to calculate row[j]. This achieves \(O(k^2)\) time and \(O(k)\) space.
Question: Can you get the \(k\)-th row in \(O(k)\) time?
Solution: Yes, by using the mathematical property of combinations. The elements in row \(n\) are \(\binom{n}{0}, \binom{n}{1}, \dots, \binom{n}{n}\). We can calculate the next element from the previous element in \(O(1)\) time using the relation: \(C(n, k) = C(n, k-1) \times \frac{n - k + 1}{k}\). We start with \(1\), and apply this formula to generate the row in exactly \(O(k)\) time and \(O(k)\) space.