题解 | #二叉树的个数#
二叉树的个数
http://www.nowcoder.com/practice/78bdfba0a5c1488a9aa8ca067ce508bd
描述
已知一棵节点个数为 的二叉树的中序遍历单调递增, 求该二叉树能能有多少种树形, 输出答案对 109+7取模
示例1
输入:1
返回值:1
示例2
输入:2
返回值:2
示例3
输入:4
返回值:14
备注:方法一
思路
递归
首先介绍一个数学知识,两数乘积之模等于两数模之乘积;对证明感兴趣的同学可以百度。
我们容易知道当n = 1时,满足条件的二叉树形状只有一个,当n = 2时有两个;
当n = 3时,有以下的形状:
可以看见当根节点为1时,所有节点均只能在右子树中,且数量为2,当根节点为3时所有节点均只能在左子树中,且形状数量为2,当根节点为2时,小于2的在左子树,大于2的在右子树,且形状数量为1。
再回过头来看题目要求,升序的中序序列,是不是有点像一个二叉排序树,左子树的所有节点均小于根节点,右子树的所有节点均大于根节点,所以本题实际上是求节点数为n的二叉排序树的形状数量。
故对于一个节点数为n的二叉排序树,当根节点值为i时,左子树有i-1个节点,右子树有n-i-1个节点,且左子树与右子树均为二叉排序树,所以当根节点值为i时,二叉排序树总共有种形状当然需要取模,那么对于一个节点数为n的二叉排序树其形状数量为
当二叉树节点数为0时,默认为1,便于计算,所以可以依据上述公式,递归计算给定数量的二叉排序树的形状数。
代码如下:
import java.util.*; public class Solution { /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * 计算二叉树个数 * @param n int整型 二叉树结点个数 * @return int整型 */ private static int mod = 1000000007; public int numberOfTree (int n) { long count = calculate(n); return (int)(count % mod); } private long calculate(int n){ // n为0返回1 if (n == 0){ return 1; } // 直接返回 if (n == 1 || n == 2){ return n; } long count = 0; // 递归计算子二叉树的树形个数 for (int i = 1;i <= n;++i){ count = count + calculate(n-i)*calculate(i-1); count = count % mod; } return count; } }
- 时间复杂度:,递归计算每次递归都要经历一重循环;
- 空间复杂度:,递归调用栈深度为n。
方法二
思路
- 动态规划,记忆表。
- 方法一的时间复杂度过大,运行超时,且其计算过程中会有很多冗余计算,譬如说计算f(4),其需要重复计算f(3),f(2),f(1)……,故可以使用记忆表,记录计算过程中各个节点数具有的二叉排序树的形状数量。
- 创建dp数组,借助方法一的递推公式进行计算。
- 在计算过程中直接取模,避免乘法溢出。
- 代码如下:
import java.util.*; public class Solution { /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * 计算二叉树个数 * @param n int整型 二叉树结点个数 * @return int整型 */ private static int mod = 1000000007; public int numberOfTree (int n) { // 直接返回 if (n == 1 || n == 2){ return n; } long[] dp = new long[n+1]; dp[0] = 1; dp[1] = 1; dp[2] = 2; for (int i = 3;i < dp.length;++i){ long count = 0; for (int j = 1;j <= i;++j){ count += ((dp[i-j]*dp[j-1])%mod); } dp[i] = (int) (count % mod); } return (int) dp[n]; }
- 时间复杂度:,双重循环;
- 空间复杂度:,一维数组辅助运算。