DeepSeek LeetCode 1569.将子数组重新排序得到同一个二叉搜索树的方案数 public int numOfWays(int[] nums)
这个问题要求计算有多少种不同的插入顺序,能生成与给定顺序 nums 完全相同的二叉搜索树(BST)。关键在于:根节点必须最先插入,而左右子树内部的相对顺序必须保持不变,但左右子树的元素可以任意交错插入。
思路概述
1. 对于给定的 nums,第一个元素一定是整棵 BST 的根节点。
2. 剩下的元素按与根的大小关系分为左子树(小于根)和右子树(大于根),且它们在原数组中的相对顺序保持不变。
3. 递归地,左子树和右子树各自也会形成一棵子 BST,其生成方案数可以同样递归计算。
4. 假设左子树有 m 个节点,右子树有 n 个节点,且左右子树的合法序列数分别为 waysL 和 waysR。在根节点已经插入的前提下,左右子树的元素可以按任意顺序交错插入,只要保持各自内部的相对顺序。交错的方式数为组合数 C(m + n, m)(即在 m+n 个位置中选择 m 个放置左子树元素,其余放右子树元素)。
5. 因此,总方案数为 waysL * waysR * C(m + n, m)。最终结果需要减去原始顺序这一种情况,并对 10^9 + 7 取模。
代码实现(Java)
```java
class Solution {
private static final int MOD = 1_000_000_007;
private long[][] C; // 组合数表
public int numOfWays(int[] nums) {
int n = nums.length;
// 预处理组合数 C[i][j] = i 选 j (模 MOD)
C = new long[n + 1][n + 1];
for (int i = 0; i <= n; i++) {
C[i][0] = C[i][i] = 1;
for (int j = 1; j < i; j++) {
C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % MOD;
}
}
// 转为 List 方便递归划分
List<Integer> list = new ArrayList<>();
for (int num : nums) list.add(num);
long total = dfs(list);
// 减去原始顺序,并处理负数取模
return (int) ((total - 1 + MOD) % MOD);
}
private long dfs(List<Integer> nums) {
if (nums.size() <= 1) return 1; // 空树或单节点树只有 1 种顺序
int root = nums.get(0);
List<Integer> left = new ArrayList<>();
List<Integer> right = new ArrayList<>();
// 按与根的大小关系划分为左右子树,保持原相对顺序
for (int i = 1; i < nums.size(); i++) {
int val = nums.get(i);
if (val < root) left.add(val);
else right.add(val);
}
long leftWays = dfs(left);
long rightWays = dfs(right);
int m = left.size();
int n = right.size();
long comb = C[m + n][m]; // 左右子树元素的交错组合数
return (leftWays * rightWays % MOD) * comb % MOD;
}
}
```
复杂度分析
· 时间复杂度:最坏情况(树退化为链表)下,每次递归需遍历当前子数组,总时间 O(n^2),其中 n 为数组长度(n ≤ 1000)。组合数预处理 O(n^2),整体在题目限制内完全可行。
· 空间复杂度:组合数表占用 O(n^2) 空间,递归调用栈深度 O(n)。
更多推荐




所有评论(0)