给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。
示例 1:
输入: n = 3
输出: 5
示例 2:
输入: n = 1
输出: 1
提示:
1 <= n <= 19
解题思路
本题可以想到的一个最直接的思路就是根据给定的 n 获取 1~n的 n 个节点,然后根据 n 个节点构建二叉搜索树,返回可以构建的数量即可。
但是本题其实不需要返回构建的树,只需要返回构建的树的种数,所以如果按以上思路解题,虽然可以得到结果,但是效率会很低,也没有必要。那如何才能在不构建树的情况下还能得到树的数量呢?
其实 n 个节点组成的二叉搜索树的种数,可以拆解为以 1 为根,2 为根 … n 为根的二叉搜索树的种数之和。同时每个值为根的二叉搜索树的数量等于可能的左子树的数量 * 可能的右子树的数量。
接下来我们看一下 n 不同的时候,每个值为根的左右子树节点的数量关系。
我们可以根据左右子树节点数量及之前推导出的 n 对应的树的种数推导后面的 n 对应的树的种数。
所以本题不需要真的进行树的构建,只需要根据以 1~n 的每一个值为根,求得左子树的可能数量,右子树的可能数量,进行相乘,就得到了以当前值为根的数量,1~n 的数量相加,就是 n 个节点组成二叉搜索树的数量。
动画演示
代码实现
var numTrees = function(n) {
// 初始化 整数=>种数数组
const arr = Array(n+1).fill(0);
arr[0] = 1,arr[1] = 1;
// 通过现有结果推导后续结果
for(let i = 2;i<=n;i++){
// 获取 1~i 每一个值为根组成二叉搜索树的数量
for(let j = 1;j<=i;j++){
arr[i] += arr[j-1]*arr[i-j]
}
}
return arr[n]
};
对称优化
根据上面的图我们可以看到,当 n = 4 的时候,以 1 为根和以 4 为根的左右子树的数量是对称的,又因为当前值为根的二叉搜索树的数量等于可能的左子树的数量 * 可能的右子树的数量,所以其实 以 1 为根和以 4 为根的二叉搜索树的数量是相等的。
根据这样的一个性质,我们在求解 n 对应的二叉搜索树的数量的时候,只需要求得前半部分的数量然后 *2 即可。这里要注意的是 n 为奇数的情况,需要单独处理一下中间值为根的二叉搜索树的数量。
代码实现
// 根据对称性质优化
var numTrees = function(n) {
// 初始化 整数=>种数数组
const arr = Array(n+1).fill(0);
arr[0] = 1,arr[1] = 1;
// 通过现有结果推导后续结果
for(let i = 2;i<=n;i++){
const mid = i>>1;
// 获取 1~mid 每一个值为根组成二叉搜索树的数量
for(let j = 1;j<=i;j++){
arr[i] += arr[j-1]*arr[i-j]
}
// 数量 *2 获取对称后半部分的数量
arr[i] *= 2;
// 如果 i 为奇数,获取中间值为根的二叉搜索树的数量
if(i%2) arr[i] += arr[mid]*arr[i-mid-1]
}
// 返回 n 对应二叉搜索树种数
return arr[n]
};
至此我们就完成了 leetcode-96-不同的二叉搜索树
如有任何问题或建议,欢迎留言讨论!