Below is the implementation of the tree structure counter:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121/** * Recursive approach (brute force) * Time Complexity: O(4^n / √n) - Exponential due to repeated calculations * Space Complexity: O(n) - Recursion stack * * @param {number} n - Number of nodes * @return {number} - Number of unique BSTs */function numTreesRecursive(n) { // Base case: empty tree or single node tree if (n === 0 || n === 1) { return 1; } let total = 0; // Try each value as the root for (let i = 1; i <= n; i++) { // Number of unique left subtrees * Number of unique right subtrees const leftTrees = numTreesRecursive(i - 1); const rightTrees = numTreesRecursive(n - i); total += leftTrees * rightTrees; } return total;} /** * Memoization approach (top-down dynamic programming) * Time Complexity: O(n^2) - Each value of n is computed once, with n iterations for each * Space Complexity: O(n) - For memoization and recursion stack * * @param {number} n - Number of nodes * @return {number} - Number of unique BSTs */function numTreesMemo(n) { // Create memo array to store results const memo = new Array(n + 1).fill(-1); function calculate(n) { // If already computed, return from memo if (memo[n] !== -1) { return memo[n]; } // Base case: empty tree or single node tree if (n === 0 || n === 1) { return 1; } let total = 0; // Try each value as the root for (let i = 1; i <= n; i++) { // Number of unique left subtrees * Number of unique right subtrees const leftTrees = calculate(i - 1); const rightTrees = calculate(n - i); total += leftTrees * rightTrees; } // Store result in memo memo[n] = total; return total; } return calculate(n);} /** * Tabulation approach (bottom-up dynamic programming) * Time Complexity: O(n^2) - Two nested loops * Space Complexity: O(n) - For the DP array * * @param {number} n - Number of nodes * @return {number} - Number of unique BSTs */function numTreesDP(n) { // Create DP array const dp = new Array(n + 1).fill(0); // Base cases dp[0] = 1; dp[1] = 1; // Fill DP array bottom-up for (let i = 2; i <= n; i++) { for (let j = 1; j <= i; j++) { // Number of unique BSTs with root j // Left subtree: j-1 nodes, Right subtree: i-j nodes dp[i] += dp[j - 1] * dp[i - j]; } } return dp[n];} /** * Mathematical approach (Catalan Numbers) * Time Complexity: O(n) - Single loop * Space Complexity: O(1) - Constant extra space * * @param {number} n - Number of nodes * @return {number} - Number of unique BSTs */function numTrees(n) { // C(n) = C(n-1) * (4n - 2) / (n + 1) let result = 1; for (let i = 1; i <= n; i++) { result = result * (4 * i - 2) / (i + 1); } return Math.round(result); // Round to handle potential floating-point errors} // Test casesconsole.log(numTrees(1)); // 1console.log(numTrees(2)); // 2console.log(numTrees(3)); // 5console.log(numTrees(4)); // 14console.log(numTrees(5)); // 42
Let's break down the implementation:
Implement the tree structure counter solution in different programming languages.
Below is the implementation of the tree structure counter in different programming languages. Select a language tab to view the corresponding code.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121/** * Recursive approach (brute force) * Time Complexity: O(4^n / √n) - Exponential due to repeated calculations * Space Complexity: O(n) - Recursion stack * * @param {number} n - Number of nodes * @return {number} - Number of unique BSTs */function numTreesRecursive(n) { // Base case: empty tree or single node tree if (n === 0 || n === 1) { return 1; } let total = 0; // Try each value as the root for (let i = 1; i <= n; i++) { // Number of unique left subtrees * Number of unique right subtrees const leftTrees = numTreesRecursive(i - 1); const rightTrees = numTreesRecursive(n - i); total += leftTrees * rightTrees; } return total;} /** * Memoization approach (top-down dynamic programming) * Time Complexity: O(n^2) - Each value of n is computed once, with n iterations for each * Space Complexity: O(n) - For memoization and recursion stack * * @param {number} n - Number of nodes * @return {number} - Number of unique BSTs */function numTreesMemo(n) { // Create memo array to store results const memo = new Array(n + 1).fill(-1); function calculate(n) { // If already computed, return from memo if (memo[n] !== -1) { return memo[n]; } // Base case: empty tree or single node tree if (n === 0 || n === 1) { return 1; } let total = 0; // Try each value as the root for (let i = 1; i <= n; i++) { // Number of unique left subtrees * Number of unique right subtrees const leftTrees = calculate(i - 1); const rightTrees = calculate(n - i); total += leftTrees * rightTrees; } // Store result in memo memo[n] = total; return total; } return calculate(n);} /** * Tabulation approach (bottom-up dynamic programming) * Time Complexity: O(n^2) - Two nested loops * Space Complexity: O(n) - For the DP array * * @param {number} n - Number of nodes * @return {number} - Number of unique BSTs */function numTreesDP(n) { // Create DP array const dp = new Array(n + 1).fill(0); // Base cases dp[0] = 1; dp[1] = 1; // Fill DP array bottom-up for (let i = 2; i <= n; i++) { for (let j = 1; j <= i; j++) { // Number of unique BSTs with root j // Left subtree: j-1 nodes, Right subtree: i-j nodes dp[i] += dp[j - 1] * dp[i - j]; } } return dp[n];} /** * Mathematical approach (Catalan Numbers) * Time Complexity: O(n) - Single loop * Space Complexity: O(1) - Constant extra space * * @param {number} n - Number of nodes * @return {number} - Number of unique BSTs */function numTrees(n) { // C(n) = C(n-1) * (4n - 2) / (n + 1) let result = 1; for (let i = 1; i <= n; i++) { result = result * (4 * i - 2) / (i + 1); } return Math.round(result); // Round to handle potential floating-point errors} // Test casesconsole.log(numTrees(1)); // 1console.log(numTrees(2)); // 2console.log(numTrees(3)); // 5console.log(numTrees(4)); // 14console.log(numTrees(5)); // 42
First, understand that we need to count the number of structurally unique BSTs that can be formed with n nodes labeled from 1 to n.
Recognize that for each root value i, the left subtree contains nodes 1 to i-1, and the right subtree contains nodes i+1 to n.
Start with a recursive solution to understand the problem better, calculating the number of unique BSTs for each possible root value.
Notice that the recursive solution has overlapping subproblems. Use memoization to avoid redundant calculations.
Transform the recursive solution into an iterative one using a bottom-up dynamic programming approach.
Observe that the solution follows the pattern of Catalan numbers, which can be calculated using a direct formula.
Use the Catalan number formula to calculate the answer directly, without using dynamic programming.
Verify the solution with different test cases, including edge cases like n = 0 and n = 1.
Modify the code to implement an alternative approach and test it with the same examples.
Implement a function that solves the tree structure counter problem using a different approach than shown above.
By convention, an empty tree (n = 0) is considered as one valid BST.
With only one node, there's only one possible BST structure.
For large values of n, be careful about integer overflow in the mathematical approach.
Below is the implementation of the tree structure counter:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121/** * Recursive approach (brute force) * Time Complexity: O(4^n / √n) - Exponential due to repeated calculations * Space Complexity: O(n) - Recursion stack * * @param {number} n - Number of nodes * @return {number} - Number of unique BSTs */function numTreesRecursive(n) { // Base case: empty tree or single node tree if (n === 0 || n === 1) { return 1; } let total = 0; // Try each value as the root for (let i = 1; i <= n; i++) { // Number of unique left subtrees * Number of unique right subtrees const leftTrees = numTreesRecursive(i - 1); const rightTrees = numTreesRecursive(n - i); total += leftTrees * rightTrees; } return total;} /** * Memoization approach (top-down dynamic programming) * Time Complexity: O(n^2) - Each value of n is computed once, with n iterations for each * Space Complexity: O(n) - For memoization and recursion stack * * @param {number} n - Number of nodes * @return {number} - Number of unique BSTs */function numTreesMemo(n) { // Create memo array to store results const memo = new Array(n + 1).fill(-1); function calculate(n) { // If already computed, return from memo if (memo[n] !== -1) { return memo[n]; } // Base case: empty tree or single node tree if (n === 0 || n === 1) { return 1; } let total = 0; // Try each value as the root for (let i = 1; i <= n; i++) { // Number of unique left subtrees * Number of unique right subtrees const leftTrees = calculate(i - 1); const rightTrees = calculate(n - i); total += leftTrees * rightTrees; } // Store result in memo memo[n] = total; return total; } return calculate(n);} /** * Tabulation approach (bottom-up dynamic programming) * Time Complexity: O(n^2) - Two nested loops * Space Complexity: O(n) - For the DP array * * @param {number} n - Number of nodes * @return {number} - Number of unique BSTs */function numTreesDP(n) { // Create DP array const dp = new Array(n + 1).fill(0); // Base cases dp[0] = 1; dp[1] = 1; // Fill DP array bottom-up for (let i = 2; i <= n; i++) { for (let j = 1; j <= i; j++) { // Number of unique BSTs with root j // Left subtree: j-1 nodes, Right subtree: i-j nodes dp[i] += dp[j - 1] * dp[i - j]; } } return dp[n];} /** * Mathematical approach (Catalan Numbers) * Time Complexity: O(n) - Single loop * Space Complexity: O(1) - Constant extra space * * @param {number} n - Number of nodes * @return {number} - Number of unique BSTs */function numTrees(n) { // C(n) = C(n-1) * (4n - 2) / (n + 1) let result = 1; for (let i = 1; i <= n; i++) { result = result * (4 * i - 2) / (i + 1); } return Math.round(result); // Round to handle potential floating-point errors} // Test casesconsole.log(numTrees(1)); // 1console.log(numTrees(2)); // 2console.log(numTrees(3)); // 5console.log(numTrees(4)); // 14console.log(numTrees(5)); // 42
Let's break down the implementation:
Implement the tree structure counter solution in different programming languages.
Below is the implementation of the tree structure counter in different programming languages. Select a language tab to view the corresponding code.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121/** * Recursive approach (brute force) * Time Complexity: O(4^n / √n) - Exponential due to repeated calculations * Space Complexity: O(n) - Recursion stack * * @param {number} n - Number of nodes * @return {number} - Number of unique BSTs */function numTreesRecursive(n) { // Base case: empty tree or single node tree if (n === 0 || n === 1) { return 1; } let total = 0; // Try each value as the root for (let i = 1; i <= n; i++) { // Number of unique left subtrees * Number of unique right subtrees const leftTrees = numTreesRecursive(i - 1); const rightTrees = numTreesRecursive(n - i); total += leftTrees * rightTrees; } return total;} /** * Memoization approach (top-down dynamic programming) * Time Complexity: O(n^2) - Each value of n is computed once, with n iterations for each * Space Complexity: O(n) - For memoization and recursion stack * * @param {number} n - Number of nodes * @return {number} - Number of unique BSTs */function numTreesMemo(n) { // Create memo array to store results const memo = new Array(n + 1).fill(-1); function calculate(n) { // If already computed, return from memo if (memo[n] !== -1) { return memo[n]; } // Base case: empty tree or single node tree if (n === 0 || n === 1) { return 1; } let total = 0; // Try each value as the root for (let i = 1; i <= n; i++) { // Number of unique left subtrees * Number of unique right subtrees const leftTrees = calculate(i - 1); const rightTrees = calculate(n - i); total += leftTrees * rightTrees; } // Store result in memo memo[n] = total; return total; } return calculate(n);} /** * Tabulation approach (bottom-up dynamic programming) * Time Complexity: O(n^2) - Two nested loops * Space Complexity: O(n) - For the DP array * * @param {number} n - Number of nodes * @return {number} - Number of unique BSTs */function numTreesDP(n) { // Create DP array const dp = new Array(n + 1).fill(0); // Base cases dp[0] = 1; dp[1] = 1; // Fill DP array bottom-up for (let i = 2; i <= n; i++) { for (let j = 1; j <= i; j++) { // Number of unique BSTs with root j // Left subtree: j-1 nodes, Right subtree: i-j nodes dp[i] += dp[j - 1] * dp[i - j]; } } return dp[n];} /** * Mathematical approach (Catalan Numbers) * Time Complexity: O(n) - Single loop * Space Complexity: O(1) - Constant extra space * * @param {number} n - Number of nodes * @return {number} - Number of unique BSTs */function numTrees(n) { // C(n) = C(n-1) * (4n - 2) / (n + 1) let result = 1; for (let i = 1; i <= n; i++) { result = result * (4 * i - 2) / (i + 1); } return Math.round(result); // Round to handle potential floating-point errors} // Test casesconsole.log(numTrees(1)); // 1console.log(numTrees(2)); // 2console.log(numTrees(3)); // 5console.log(numTrees(4)); // 14console.log(numTrees(5)); // 42
First, understand that we need to count the number of structurally unique BSTs that can be formed with n nodes labeled from 1 to n.
Recognize that for each root value i, the left subtree contains nodes 1 to i-1, and the right subtree contains nodes i+1 to n.
Start with a recursive solution to understand the problem better, calculating the number of unique BSTs for each possible root value.
Notice that the recursive solution has overlapping subproblems. Use memoization to avoid redundant calculations.
Transform the recursive solution into an iterative one using a bottom-up dynamic programming approach.
Observe that the solution follows the pattern of Catalan numbers, which can be calculated using a direct formula.
Use the Catalan number formula to calculate the answer directly, without using dynamic programming.
Verify the solution with different test cases, including edge cases like n = 0 and n = 1.
Modify the code to implement an alternative approach and test it with the same examples.
Implement a function that solves the tree structure counter problem using a different approach than shown above.
By convention, an empty tree (n = 0) is considered as one valid BST.
With only one node, there's only one possible BST structure.
For large values of n, be careful about integer overflow in the mathematical approach.