leetcode:96. 不同的二叉搜索树

题目来源

96. 不同的二叉搜索树

题目描述

leetcode:96. 不同的二叉搜索树_第1张图片

class Solution {
public:
    int numTrees(int n){
		
	}

题目解析

动态规划

分析

给定一个数n,怎么去构建一颗二叉搜索树呢

  • 我们在构建树时,都是先选定根节点,然后在递归的去构建左右子树的。
  • 假设给定了一个5,而且对于序列[1,5],我们应该怎么构建树呢?
    • 假设根节点为5,那么如果要是一颗二叉搜索树,那么剩下的部分[1, 4]都必须是它的左子树
    • 假设根节点为4,那么如果要是一颗二叉搜索树,那么剩下的部分[1, 3]必须是它的左子树,[5]必须是它的右子树
    • 假设根节点为3,那么如果要是一颗二叉搜索树,那么剩下的部分[1, 2]必须是它的左子树,[4, 5]必须是它的右子树
    • 假设根节点为2,那么如果要是一颗二叉搜索树,那么剩下的部分[1]必须是它的左子树,[3, 5]必须是它的右子树
    • 假设根节点为1,那么如果要是一颗二叉搜索树,那么剩下的部分[2, 5]必须是它的右子树
  • 对于序列[1,4],我们该怎么构建树呢?
    • 假设根节点为4,那么如果要是一颗二叉搜索树,那么剩下的部分[1, 3]都必须是它的左子树
    • 假设根节点为3,那么如果要是一颗二叉搜索树,那么剩下的部分[1, 2]必须是它的左子树,[4,5]必须是它的右子树
  • 从上面可以看出,有大量的重叠子问题,所以可以用递归或者动态规划来解决。

对于上面分析,我们用书面语言来总结一下:

  • 给定一个有序序列 1... n 1...n 1...n,为了构建出一颗二叉树,我们可以遍历每个数字i,然后将该数字作为树根,将1...(i-1)序列作为左子树,将(i + 1) ... n序列作为右子树。接着我们可以按照同样的方式递归构建左子树和右子树。
  • 在上述构造过程中,由于根的值不同,因此我们可以保证每颗二叉搜索树是唯一的。
  • 由此可见,原问题可以分解成规模较小的两个子问题,且子问题的解可以复用。因此,我们可以想到使用动态规划来求解本题。

从上面可以看出,对于一个数5,它的最优子结构是

  • 以1为根节点,构建的二叉搜索树有多少个
  • 以2为根节点,构建的二叉搜索树有多少个
  • 以3为根节点,构建的二叉搜索树有多少个
  • 以4为根节点,构建的二叉搜索树有多少个
  • 以5为根节点,构建的二叉搜索树有多少个

那么,设以i为根节点的二叉搜索树为f(i),那么其最优子结构和原问题有什么关系呢?假设给定长度为5的序列,那么

  • 长度为5的序列能构成的不同二叉搜索树的个数 = 以5为根节点,构建的二叉搜索树数量 + 以4为根节点,构建的二叉搜索树数量 + 以3为根节点,构建的二叉搜索树数量 + 以2为根节点,构建的二叉搜索树数量 + 以1为根节点,构建的二叉搜索树数量 =f(1) + f(2) + f(3) + f(4) + f(5)

思路

(1)定义状态

由于题目是要求我们计算不同二叉搜索树的个数,因此我们可以定义两个函数:

  • G(n):长度为n的序列能构成的不同二叉搜索树的个数
  • f(i):以i为根的二叉搜索树的个数。

则有
G ( n ) = f ( 1 ) + f ( 2 ) + f ( 3 ) + . . . f ( n ) G(n)=f(1)+f(2)+f(3)+...f(n) G(n)=f(1)+f(2)+f(3)+...f(n)

(2)推导公式。关键要推导出f(i)。

  • 当i为根节点时,其左子树节点个数为[1,2,3...i-1],右子树节点个数为[i+1, i+2,... n]
  • 所以当i为根节点时,其左子树节点个数为i-1个,右子树节点为n-i个,即f(i) = G(i-1)*G(n-i)
  • 因此
    G ( n ) = G ( 0 ) ∗ G ( n − 1 ) + G ( 1 ) ∗ G ( n − 2 ) + . . . + G ( n − 1 ) ∗ G ( 0 ) G(n) = G(0)*G(n-1)+G(1)*G(n-2)+...+G(n-1)*G(0) G(n)=G(0)G(n1)+G(1)G(n2)+...+G(n1)G(0)

(3)对于边界情况

  • f ( 0 ) f(0) f(0),也就是没有节点存在时,此时只有一颗空树,f(0) = 1
  • f ( 1 ) f(1) f(1),也就是以1为根节点时,只有节点都为空树,f(1) = 1
  • 因此
    G ( 0 ) = 1 G(0) = 1 G(0)=1
    G ( 1 ) = 1 G(1) = 1 G(1)=1
                    1                        n = 1

                2        1                   n = 2
               /          \
              1            2
  
   1         3     3      2      1           n = 3
    \       /     /      / \      \
     3     2     1      1   3      2
    /     /       \                 \
   2     1         2                 3

总结

leetcode:96. 不同的二叉搜索树_第2张图片

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

leetcode:96. 不同的二叉搜索树_第3张图片

你可能感兴趣的:(算法与数据结构,leetcode)