Algorithm

Pascal's Triangle

Arrays & Strings Pattern

Pascal's Triangle

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.

CONSTRAINTS
  • 1 <= numRows <= 30
EXAMPLE 1
Input: numRows = 5
Output: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]
Each interior element is the sum of the two elements above it.
EXAMPLE 2
Input: numRows = 1
Output: [[1]]
First row is always just [1].
EXAMPLE 3
Input: numRows = 3
Output: [[1],[1,1],[1,2,1]]
Row 2: 1+1=2 in the middle.
Just to clarify the output format, you want an array of arrays representing the whole triangle, not just a specific row?
Correct, you should return a list containing all the rows up to `numRows`.
Are the values in the triangle guaranteed to fit within a standard 32-bit signed integer?
Yes, `numRows` is small enough (<= 30) that you do not need to worry about integer overflow.

Pascal's triangle is a stack of rows of numbers. Row 0 is just [1]. Every later row is one entry longer than the previous, starts and ends with 1, and every interior entry equals the sum of the two entries sitting diagonally above it — above-left plus above-right. Row 3 is [1, 3, 3, 1] because each 3 is the sum of the 1 and 2 above it. The task: return the first numRows rows as a list of lists. This is our first two-dimensional output: a list where each element is itself a list (one per row).

Why "compute each cell on demand" explodes

The definition looks recursive, so the naive move is a function that computes any cell by asking for the two cells above it:

python
def get_val(r, c):
    if c == 0 or c == r: return 1              # edges are always 1
    return get_val(r-1, c-1) + get_val(r-1, c)

Trace what actually happens: computing one cell in row 10 spawns two calls into row 9, four into row 8, eight into row 7... the call count roughly doubles per level, and — this is the real crime — the same cells get recomputed over and over, because two neighbors in row r both ask for the cell they share in row r-1. Exponential work to rebuild values we already produced moments earlier.

Store what you compute; parents are already waiting

Flip the direction. Instead of starting at the target and asking upward, build downward from row 0 and keep every row you finish. When row i is being built, its entire parent row i-1 already sits in memory — every interior entry is one addition, no recursion, no recomputation. This is the same instinct that powered Kadane's algorithm: store the answer to the previous subproblem and build the next answer from it directly. Here the subproblem is simply "the previous row".

python
triangle = [[1]]
for i in range(1, num_rows):
    prev = triangle[i-1]           # the parent row, fully computed
    curr = [1]                     # every row starts with 1
    for j in range(1, i):          # row i has i+1 entries; interior is j = 1..i-1
        curr.append(prev[j-1] + prev[j])
    curr.append(1)                 # and ends with 1
    triangle.append(curr)
return triangle
Worked Example:numRows = 4
- Row 0: [1].
- Row 1: no interior — edges only: [1, 1].
- Row 2: edge 1, interior 1 + 1 = 2, edge 1: [1, 2, 1].
- Row 3: edge 1, interior 1 + 2 = 3 and 2 + 1 = 3, edge 1: [1, 3, 3, 1].
- Answer: [[1], [1,1], [1,2,1], [1,3,3,1]].
Why O(numRows²) is not just good — it is optimal

Total entries produced: 1 + 2 + ... + numRows, about numRows²/2. Each costs one addition, so O(numRows²) time and space. Could a cleverer algorithm be faster? No — and the reason is worth internalizing: the output itself contains O(numRows²) numbers, and no algorithm can run faster than the time needed to write its own answer down. When output size matches your runtime, you are done optimizing. That "compare against the size of the answer" check is a habit that instantly tells you whether further optimization is even possible.

Interactive Strategy Visualization

Triangle Construction

Dynamic Programming Strategy
Click Play to generate the triangle
RECURRENCE RELATION

Each cell (i, j) = Cell (i-1, j-1) + Cell (i-1, j). This symmetry builds the entire structure from the edges inward.

EFFICIENCY

We process O(N²) cells in total. By building layer by layer, we avoid redundant calculations—the core of Dynamic Programming.

Exponential Recursion
O(numRows²) Row-by-Row — Optimal (output is that size)