Dynamic Programming · 5 of 60
Program for nth Catalan Number
Count how many different tree shapes you can build from n nodes, the famous number that also counts valid bracket strings and many other things.
- Simple way
- time O(3^n)
room O(n) - Clever way
- time O(n^2)
room O(n)
The problem, simply
Find the nth Catalan number C(n). The numbers go 1, 1, 2, 5, 14, 42, 132, ... for n = 0, 1, 2, 3, 4, 5, 6.
The easiest way to picture them: C(n) is how many different binary search trees you can build with the keys 1 to n. The rule that builds them is C(0) = 1 and C(n) = C(0) x C(n-1) + C(1) x C(n-2) + ... + C(n-1) x C(0).
A story to remember it
Picture it
All 5 search trees with keys 1, 2 and 3. So C(3) = 5. Top 1 gives 2 shapes, top 2 gives 1, top 3 gives 2.
- looking now
Examples
Three nodes
- In
n = 3- Out
5
The five trees in the picture. It also counts the 5 valid strings of 3 bracket pairs: ((())), (()()), (())(), ()(()), ()()().
Five nodes
- In
n = 5- Out
42
The walkthrough builds it from the smaller numbers.
Zero
- In
n = 0- Out
1
The empty tree counts as one shape. This base case makes the formula work.
One
- In
n = 1- Out
1
One node, one shape.
Grows fast
- In
n = 10- Out
16796
Catalan numbers grow roughly like 4^n. C(35) is the last one that fits in a Java long.
Way 1: the simple way
Code the rule as it is written.
- If
n <= 1, return 1. - For each top
ifrom 0 ton - 1, addC(i) x C(n - 1 - i).
The calls for C(3). L and R mark the left and right family sizes. C(2) is worked out twice, and the small ones many times. For bigger n this repeats wildly.
- checking
class Solution {
public long catalan(int n) {
if (n <= 1) return 1;
long total = 0;
for (int i = 0; i < n; i++) {
total += catalan(i) * catalan(n - 1 - i); // left family x right family
}
return total;
}
}How long it takes: each call makes calls for every smaller size, twice. The count of calls grows like O(3^n). Extra room: the call chain, O(n).
Way 2: the clever way
Fill a row C[0..n] from left to right. When you reach box i, every smaller box is already filled.
C[0] = 1,C[1] = 1.- For
i = 2ton:C[i] = sum of C[j] x C[i - 1 - j]forj = 0toi - 1. Herejnodes go left andi - 1 - jgo right. - Return
C[n].
Fill the Catalan row up to C(5)
01 / 08
C(0) = 1 and C(1) = 1. An empty tree is one shape, and a single node is one shape.
- looking now
- checking
- answer
- just added
class Solution {
public long catalan(int n) {
long[] C = new long[n + 2];
C[0] = 1;
C[1] = 1;
for (int i = 2; i <= n; i++) {
for (int j = 0; j < i; j++) {
C[i] += C[j] * C[i - 1 - j]; // j on the left, i-1-j on the right
}
}
return C[n];
}
}How long it takes: box i costs i steps, so O(n^2) in all. Extra room: the row, O(n).
Another way: one loop with the formula
There is a shortcut formula: C(n) = C(2n, n) / (n + 1). It gives a neat step from one Catalan number to the next: C(i) = C(i - 1) x 2(2i - 1) / (i + 1). Multiply first, then divide, and the division is always exact.
class Solution {
public long catalan(int n) {
long c = 1; // C(0)
for (int i = 1; i <= n; i++) {
c = c * 2 * (2 * i - 1) / (i + 1); // exact division
}
return c;
}
}This is O(n) time and O(1) room. The product before the division overflows a little earlier than the answer does (around n = 33), so use BigInteger or modular inverses for big n.
Watch out for
- The top node itself is not in either family. That is why the sizes add up to
n - 1, notn. C[1]must exist even whenn = 0. Size the rown + 2, or handlen = 0first.- The numbers overflow
longafterC(35). With a modulo, you cannot use the division formula directly. Use theO(n^2)row, or modular inverses.
What interviewers ask next
- Where else does
C(n)show up? Valid bracket strings withnpairs, ways to cut a polygon withn + 2corners into triangles, mountain ranges withnup steps andndown steps, and paths in annbyngrid that never cross the diagonal. - Unique BSTs (LeetCode 96) is exactly
C(n). Unique BSTs II asks to build all the trees: recursion over the top, combining every left tree with every right tree. - Count full binary trees with
nleaves. That isC(n - 1). - How fast does it grow? About
4^n / (n^1.5 x sqrt(pi)).
Remember this
Grown-up words: the families are subtrees, the shapes are binary search trees, the rule is the Catalan recurrence, the filled row is 1D dynamic programming, and the shortcut uses the binomial coefficient.