给你一个数组 nums 表示 1 到 n 的一个排列。我们按照元素在 nums
中的顺序依次插入一个初始为空的二叉查找树(BST)。请你统计将 nums 重新排序后,统计满足如下条件的方案数:重排后得到的二叉查找树与nums 原本数字顺序得到的二叉查找树相同。
比方说,给你 nums = [2,1,3],我们得到一棵 2为根,1为左孩子,3 为右孩子的树。数组 [2,3,1] 也能得到相同的BST,但 [3,2,1] 会得到一棵不同的 BST 。
请你返回重排 nums 后,与原数组 nums 得到相同二叉查找树的方案数。
由于答案可能会很大,请将结果对 10^9 + 7 取余数。
示例 1:
输入:nums = [2,1,3] 输出:1 解释:我们将 nums 重排, [2,3,1] 能得到相同的 BST 。没有其他得到相同BST 的方案了。
示例 2:
输入:nums = [3,4,5,1,2] 输出:5 解释:下面 5 个数组会得到相同的 BST: [3,1,2,4,5]
[3,1,4,2,5] [3,1,4,5,2] [3,4,1,2,5] [3,4,1,5,2]
示例 3:
输入:nums = [1,2,3] 输出:0 解释:没有别的排列顺序能得到相同的 BST 。
示例 4:
输入:nums = [3,1,2,5,4,6] 输出:19
提示:
1 <= nums.length <= 1000 1 <= nums[i] <= nums.length
nums 中所有数互不相同 。
根节点之后,左树右树彼此之间穿插的顺序无关紧要。所以分别得到左树和右树的答案,相乘再乘一个排列数就好。题目中给的mod值很大,mod*mod就会造成溢出,偷懒使用Java的Biginteger进行计算了。
import java.math.BigInteger;
class Solution {
private final BigInteger mod = BigInteger.valueOf(1000000007);
BigInteger nCm(int n, int m) {
//从 m 里选 n 个
BigInteger ans = BigInteger.ONE;
for(int i = 0; i < n; ++i) {
ans = ans.multiply(BigInteger.valueOf(m - i));
}
for(int i = 1; i < n + 1; ++i) {
ans = ans.divide(BigInteger.valueOf(i));
}
return ans.mod(mod);
}
public int numOfWays(int[] nums) {
int ret = numOfWaysRec(nums) - 1;
return ret != 0 ? ret : ret % 1000000007;
}
public int numOfWaysRec(int[] nums) {
//意外地简单感觉
//左右子树答案分别算 乘起来再整一个组合数
if(nums.length <= 2)
return 1;
int root = nums[0];
int lValue = 0, rValue = 0;
for(int i = 1; i < nums.length; ++i) {
if(nums[i] > root)
++rValue;
else
++lValue;
}
int[] lChild = new int[lValue];
int[] rChild = new int[rValue];
rValue = lValue = 0;
for(int i = 1; i < nums.length; ++i) {
if(nums[i] > root)
rChild[rValue++] = nums[i];
else
lChild[lValue++] = nums[i];
}
BigInteger ans = BigInteger.valueOf(numOfWaysRec(rChild));
ans = ans.multiply(BigInteger.valueOf(numOfWaysRec(lChild)))
.multiply(nCm(Math.min(lValue, rValue), nums.length - 1))
.mod(mod);
return ans.intValue();
}
}