Basic Problemscountingcombinatoricsgrid

Pascal's Triangle

Given a number of rows n, generate Pascal's Triangle: a triangular array where each interior entry equals the sum of the two entries diagonally above it, with 1s running along both outer edges.

Do this lesson first: climbing stairs

Example input

numRows = 8

Expected output

[1], [1, 1], [1, 2, 1], [1, 3, 3, 1], [1, 4, 6, 4, 1], [1, 5, 10, 10, 5, 1], [1, 6, 15, 20, 15, 6, 1], [1, 7, 21, 35, 35, 21, 7, 1]

Break it down

Answer each question out loud before you open it. Getting it wrong here is the useful part. A revealed answer you never guessed at teaches you nothing.

Fill the table

The table pauses before each cell you have to supply. Type the value the recurrence gives, and the write animation confirms it.

Step not started

Press start. The animation stops at every cell YOUR recurrence must fill.

dp[r][0] = 1 and dp[r][r] = 1 on every row: the two outer edges are 1s by definition. They have to be given rather than summed, because the rule would reach for column -1 at the left end and for a position above the diagonal at the right end, and neither exists. Row 0 is the single cell where the two edges meet. Fill row by row, top to bottom; within a row the interior entries do not read each other, so left to right is as good as any order. The positions above the diagonal show a dot: they are not part of the triangle, and no interior cell can reach one, since a cell at column k < r reads columns k - 1 and k of row r - 1, both on or below that row's diagonal.

Rows ↓
Cols →
0
1
2
3
4
5
6
7
0
1
·
·
·
·
·
·
·
1
1
1
·
·
·
·
·
·
2
1
2
1
·
·
·
·
·
3
1
3
3
1
·
·
·
·
4
1
4
6
4
1
·
·
·
5
1
5
10
10
5
1
·
·
6
1
6
15
20
15
6
1
·
7
1
7
21
35
35
21
7
1

Rows ↓row r: a row of the triangle, and every row is output

Cols →column k: entry k of its row

pascals-triangle.ts
  1. 1function generate(numRows) {
  2. 2 const rows = [];
  3. 3 for (let r = 0; r < numRows; r++) {
  4. 4 const row = new Array(r + 1);
  5. 5 row[0] = 1;
  6. 6 row[r] = 1;
  7. 7 for (let k = 1; k < r; k++) {
  8. 8 const upLeft = rows[r - 1][k - 1];
  9. 9 const up = rows[r - 1][k];
  10. 10 row[k] = upLeft + up;
  11. 11 }
  12. 12 rows.push(row);
  13. 13 }
  14. 14 return rows;
  15. 15}
Base caseComputedBeing readAnswer

The code, the trap, the variations

pascals-triangle.ts
  1. 1function generate(numRows) {
  2. 2 const rows = [];
  3. 3 for (let r = 0; r < numRows; r++) {
  4. 4 const row = new Array(r + 1);
  5. 5 row[0] = 1;
  6. 6 row[r] = 1;
  7. 7 for (let k = 1; k < r; k++) {
  8. 8 const upLeft = rows[r - 1][k - 1];
  9. 9 const up = rows[r - 1][k];
  10. 10 row[k] = upLeft + up;
  11. 11 }
  12. 12 rows.push(row);
  13. 13 }
  14. 14 return rows;
  15. 15}

Where people go wrong

Carrying nth-row-of-pascals-triangle's one-array trick over to this problem. There, a single array updated from the right turns each row into the next, and that is correct because only the last row is wanted. Here, pushing that one array into the result after each pass pushes the SAME array every time, so every row of the result is one object, and when the loop finishes they all show its final contents: at numRows = 8 the result is 1, 7, 21, 35, 35, 21, 7, 1 eight times over. Nothing throws and every number in it is a real entry of the triangle, which is what makes it easy to miss in a quick look at the last row. Push a copy each pass, or build a fresh row as the snippet does.

  • Return only the last row instead of every row.

    That is nth-row-of-pascals-triangle. The recurrence, the given edges and the fill order do not change. What changes is that the rows above the last stop being output, so they can be dropped as soon as the next row is built: one rolling row is O(n) space, and walking across a single row with the in-row closed form is O(n) time as well.

  • Return a single entry, C(n, k), instead of any whole row.

    That is binomial-coefficient: the same table, read at one cell. The sweep can then stop at column k, so the work is O(n k) rather than O(n^2), and everything right of column k is never written.

  • Generate the triangle modulo 2, keeping only whether each entry is odd.

    Replace the addition with an exclusive or, or add and take % 2. The shape, the given edges and the order stay exactly as they are, and the odd entries trace out the Sierpinski triangle. The values stop growing at all, which is the point of reducing modulo anything: entries of row 34 already pass 2^31, and modulo a number the table never overflows.