周末加餐 将子数组重新排序得到同一个二叉查找树的方案数

给你一个数组 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();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值