LintCode 163. Unique Binary Search Trees (Catalan数经典题!)

  1. Unique Binary Search Trees
    中文English
    Given n, how many structurally unique BSTs (binary search trees) that store values 1…n?

Example
Example 1:

Input:n = 3,
Output: 5
Explanation:there are a total of 5 unique BST’s.
1 3 3 2 1
\ / / / \
3 2 1 1 3 2
/ / \
2 1 2 3

解法1:
**思路:以i为根节点的BST树,其左子树由[0, i-1]构成, 其右子树由[i+1, n]构成。
下面参考了:
http://bangbingsyb.blogspot.com/2014/11/leetcode-unique-binary-search-trees-i-ii.html
定义f(n)为unique BST的数量,以n = 3为例:
构造的BST的根节点可以取{1, 2, 3}中的任一数字。
如以1为节点,则left subtree只能有0个节点,而right subtree有2, 3两个节点。所以left/right subtree一共的combination数量为:f(0) * f(2) = 2

以2为节点,则left subtree只能为1,right subtree只能为2:f(1) * f(1) = 1

以3为节点,则left subtree有1, 2两个节点,right subtree有0个节点:f(2)*f(0) = 2

总结规律:
f(0) = 1
f(n) = f(0)*f(n-1) + f(1)*f(n-2) + … + f(n-2)*f(1) + f(n-1)*f(0)

这些数目加起来即Catalan数。**
解法1即直接用catalan数公式求。f(n) = C(2n, n)/(n + 1)。
简化后可得f(n)=(n+2)(n+3)…(2n-1)/(34…(n-1))。

代码如下:

class Solution {
public:
    /**
     * @param n: An integer
     * @return: An integer
     */
    int numTrees(int n) {
        if (n <= 1) return 1;
        if (n == 2) return 2;
        
        double numerator = 1;
        double denominator = 1;
        int n2 = n * 2;
        
        for (int i = n + 2; i < n2; ++i) {
            numerator *= i;    
        }

        for (int i = 3; i < n; ++i) {
            denominator *= i;
        }

        return (int)ceil(numerator / denominator);
    }
};

解法2:DP

代码如下:

class Solution {
public:
    /**
     * @param n: An integer
     * @return: An integer
     */
    int numTrees(int n) {
        if (n <= 1) return 1;
        
        vector<int> dp(n + 1, 0);
        dp[0] = 1; dp[1] = 1;
        for (int i = 2; i <= n; ++i) {
            for (int j = 0; j < i; ++j) {
                dp[i] += dp[j] * dp[i - j - 1];
            }
        }
        return dp[n];
    }
};

解法3:同样是DP。
但是我们从等价的出栈顺序数问题来考虑。
dp[i][j]表示栈外面有i个数要入栈,栈内有j个数的情况。

代码如下:

class Solution {
public:
    /**
     * @param n: An integer
     * @return: An integer
     */
    int numTrees(int n) {
        if (n <= 1) return 1;
        //consider it as an equivalent # of out-stock sequence problem
        vector<vector<int>> dp(n + 1, vector<int>(n + 1, 0));  //dp[out][in]
        for (int i = 0; i <= n; ++i) dp[0][i] = 1;
        for (int i = 0; i <= n; ++i) {
            if (i > 0) dp[i][0] = dp[i - 1][1];
            for (int j = 0; j <= n - i; ++j) {
                if (i > 0 && j > 0)
                    dp[i][j] = dp[i - 1][j + 1] + dp[i][j - 1];
            }
        }
        return dp[n][0];
    }
};

解法4:解法3的递归版。

class Solution {
public:
    /**
     * @param n: An integer
     * @return: An integer
     */
    int numTrees(int n) {
        return catalan(n, 0);
 }
private:
   int catalan(int outside, int inside) {
       if (outside == 0) return 1; //stack full
       if (inside  == 0) return catalan(outside - 1, 1);  //stack empty
       return catalan(outside - 1, inside + 1) + catalan(outside, inside - 1);
   }
};

注意,上面的版本在n大的时候会超时,应该用下面的memorization版本。

class Solution {
public:
    /**
     * @param n: An integer
     * @return: An integer
     */
    int numTrees(int n) {
        catalanNums.resize(n + 1, vector<int>(n + 1, -1));
        
        for (int i = 0; i <= n; ++i) {
            for (int j = 0; j <= n - i; ++j) {
                if (catalanNums[i][j] < 0) {
                    catalanNums[i][j] = catalan(i, j);
                }
            }
        }

        return catalan(n, 0);
    }
 
private:
   int catalan(int outside, int inside) {
       if (catalanNums[outside][inside] >= 0) return catalanNums[outside][inside];
       
       if (outside == 0) return 1; //stack full
       if (inside == 0) return catalan(outside - 1, 1);  //stack empty
        
       return catalan(outside - 1, inside + 1) + catalan(outside, inside - 1);
   }
   
   vector<vector<int>> catalanNums;
};

解法5:还是DP。
但没用memorization会超时。

class Solution {
public:
    /**
     * @paramn n: An integer
     * @return: A list of root
     */
    int numTrees(int n) {
        return helper(1, n);
    }

private:
    int helper(int begin, int end) {
        if (begin > end) return 1; //NULL is one solution
        int count = 0;
        for (int i = begin; i <= end; ++i) {
            int leftCount = helper(begin, i - 1); //leftTree
            int rightCount = helper(i + 1, end); //rightTree
            count += leftCount * rightCount;
        }
        return count;
    }
};

解法6: 解法5的memorization版本。
注意:解法2的dp[k]即解法6的dp[i][j] (j - i + 1 == k),即解法6的二维矩阵很多都是冗余的,即(1…5)和(2…6)所构成的BST树的数目都是一样的,即(0…4)所构成的BST树的数目。
代码如下:

class Solution {
public:
    /**
     * @paramn n: An integer
     * @return: A list of root
     */
    int numTrees(int n) {
        dp.resize(n + 1, vector<int>(n + 1, -1));
        for (int i = 0; i <= n; ++i) 
            for (int j = 0; j <=n; ++j) {
                if (i >= j) dp[i][j] = 1;   
            }
        return helper(1, n);
    }

private:
    int helper(int begin, int end) {
        if (begin >= end) return 1; //NULL is one solution
        int count = 0, leftCount = 0, rightCount = 0;
        for (int i = begin; i <= end; ++i) {
            leftCount = dp[begin][i - 1] < 0 ? helper(begin, i - 1) : dp[begin][i - 1]; //leftTree
            if (i < end) {
                rightCount = dp[i + 1][end] < 0 ? helper(i + 1, end) : dp[i + 1][end]; //rightTree
            } else {
                rightCount = 1;
            }
            count += leftCount * rightCount;
        }
        dp[begin][end] = count;
        return count;
    }
    
    vector<vector<int>> dp;
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值