Stage 4: Advanced Techniques·Combinatorics & Counting
Hard#15699 min readMathCombinatoricsRecursionLeetCode

Ways to Reorder Array to Get Same BST

Recursive split with C(n,k)

Recursive decomposition + nCr. Beautiful problem for your background.

Published Apr 16, 2026

Problem

Given an array nums of distinct integers, a BST is built by inserting elements left-to-right (the first element is the root). Return the number of different permutations of nums that would produce the same BST, modulo 10^9 + 7. The answer excludes the original ordering, so subtract 1.

Input
nums = [2, 1, 3]
Output
1
Explanation
Root is 2. Left subtree {1}, right {3}. The only other valid ordering is [2, 3, 1]. So 2 total orderings - 1 = 1.
Input
nums = [3, 4, 5, 1, 2]
Output
5
Explanation
Root is 3. Left {1, 2}, right {4, 5}. 6 valid orderings. 6 - 1 = 5.
Input
nums = [1, 2, 3]
Output
0
Explanation
Root is 1, everything goes right in a chain. Only one valid insertion order exists.

Intuition

The first element is always the root. Elements smaller go left, larger go right. Within each subtree, the relative order must produce the same sub-BST — recursively.

Given left subtree size L and right subtree size R, the number of valid interleavings of two independent sequences is C(L + R, L) — choose which positions in the combined sequence belong to the left subtree.

So: ways(node) = C(L + R, L) * ways(left) * ways(right).

The answer is ways(root) - 1 (exclude the original ordering).

Precompute Pascal's triangle for C(n, k) modular, then recurse.

Approach

Recursive Split with C(n,k)

TimeO(n²)
SpaceO(n²)
Recommended

Precompute C[n][k] via Pascal's triangle mod 10^9 + 7. Recurse: for each subtree, partition into left (smaller than root) and right (larger), multiply C(|left|+|right|, |left|) by recursive results.

impl Solution {
    pub fn num_of_ways(nums: Vec<i32>) -> i32 {
        const MOD: i64 = 1_000_000_007;
        let n = nums.len();

        // Pascal's triangle.
        let mut c = vec![vec![0i64; n + 1]; n + 1];
        for i in 0..=n {
            c[i][0] = 1;
            for j in 1..=i {
                c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % MOD;
            }
        }

        fn count(arr: &[i32], c: &[Vec<i64>]) -> i64 {
            const MOD: i64 = 1_000_000_007;
            if arr.len() <= 1 {
                return 1;
            }
            let root= arr[0];
            let left: Vec<i32> = arr[1..].iter().copied().filter(|&x| x < root).collect();
            let right: Vec<i32> = arr[1..].iter().copied().filter(|&x| x > root).collect();
            let ways = c[left.len() + right.len()][left.len()]
                % MOD
                * count(&left, c)
                % MOD
                * count(&right, c)
                % MOD;
            ways
        }

        ((count(&nums, &c) - 1 + MOD) % MOD) as i32
    }
}
Rust · runnable

Trace

nums = [3, 4, 5, 1, 2]:

rootleftrightC(L+R, L)left waysright wayssubtotal
3[1, 2][4, 5]C(4, 2) = 6116
1 (left of 3)[][2]C(1, 0) = 1111
4 (right of 3)[][5]C(1, 0) = 1111

Total = 6. Answer = 6 - 1 = 5.

Edge cases

  • Sorted input — all elements go right in a chain. Only 1 valid ordering. Answer = 0.
  • Reverse sorted — all elements go left. Same: answer = 0.
  • Two elements — root + one child. C(1, 0) = 1 or C(1, 1) = 1. Answer = 0.
  • Balanced split — maximizes the combinatorial factor; gives the largest answer.

Takeaway

BST structure is determined by relative order, not absolute values. The interleaving count C(L + R, L) captures exactly how many ways two independent sequences can be shuffled while preserving their internal orders. This is the same "shuffle" counting that appears in merge-sort analysis and multi-threaded scheduling.

More in Combinatorics & Counting