leetcode 1569. Number of Ways to Reorder Array to Get Same BST(重新排序数组以获得相同 BST 的方法数)

文章讨论了一种关于二叉搜索树(BST)的问题,即给定一个包含1到n数字的数组,如何重新排列数组以构建与初始BST相同的树。关键在于利用BST的性质,确定根节点后,固定左子树和右子树的相对顺序,然后递归计算左右子树的排列组合。杨辉三角用于高效计算组合数,避免大整数除法。最后给出了一段Python代码实现该算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述
在这里插入图片描述

数组nums中包含1~n 数字的组合。
把nums中的数字按顺序建立一个初始BST,
重新排列nums中的数字,建成的BST和初始BST相同,问有多少种排列方法。

思路:

建立BST时,index为0处的元素为root, 后面 < root 的作为左子树,> root 的作为右子树。
想要和初始BST一样,root肯定是固定不动的。

需要重新排列的是root右边的左子树和右子树。

举个例子,[4,6,5,1,2,3],
4是root, [1,2,3] 是左子树,[6,5]是右子树。
先不考虑[1,2,3],[6,5]的排列组合,先就保持这个顺序,那么这5个数字不管怎么交叉放置都是[1,2,3]是左子数,[6,5]是右子数。
比如变成[4,1,6,5,2,3], root是4不变,<4的还是[1,2,3], > 4的还是[6,5]

所以,上面的例子中,root 4右边有5个位置,选3个位置放1,2,3, 就有C53C_5^3C53种,剩下的位置放右子树的2个数字。
左子树和右子树内部又可以排列组合,
最终的排列数就是C53C_5^3C53 * combination(left) * combination(right)

左子树,右子树内部的排列组合就是递归这一过程。

下面就是如何计算组合数combination,Python可以直接用comb函数。
非python的看下面。
组合公式是Cnk=n!/k!C_n^k = n!/k!Cnk=n!/k!, 直接用阶乘 一是计算量太大;二是有除法,不能保证精确性。

不过可以观察到,组合数其实是个杨辉三角。
第一行是C00C_0^0C00, 第2行是C10C_1^0C10, C11C_1^1C11,
第3行是C20C_2^0C20, C21C_2^1C21, C22C_2^2C22, …

       1
      1 1
     1 2 1
    1 3 3 1

假设杨辉三角保存在table数组中,那么CnkC_n^kCnk=table[n][k]

总结一下,数组长度为n, root是nums[0], root右边有n-1个位置,
取其中left个位置放左子树的数字,剩下的位置放右子树的数字,
左子树和右子树再递归这一过程。
排列数会包含nums本身,所以最后要-1.

总的排列数就是(Cn−1leftC_{n-1}^{left}Cn1left * combination(left) * combination(right) - 1) % mod

这里建杨辉三角时,static会比instance函数更快

class Solution {
    private static final long MOD = 1000000007;
    //提前计算好组合数
    private static final long[][] COMB = pascalsTriangle(1000);

    public int numOfWays(int[] nums) {
        //nums转List
        List<Integer> arr = Arrays.stream(nums).boxed().collect(Collectors.toList()) ;
        
        return (int)((combination(arr)-1) % MOD);
    }

    long combination(List<Integer> nodes) {
        int n = nodes.size();
        if(n <= 2) return 1;

        List<Integer> left = new ArrayList<>();
        List<Integer> right = new ArrayList<>();

        int root = nodes.get(0);
        for(int i = 1; i < n; i++) {
            int cur = nodes.get(i);
            if(cur < root) left.add(cur);
            else right.add(cur);
        }
       
        long leftComb = combination(left) % MOD;
        long rightComb = combination(right) % MOD;
        return COMB[n-1][left.size()] * (leftComb * rightComb % MOD) % MOD;
    }

    //用static会更快
    static long[][] pascalsTriangle(int n) {
        long[][] comb = new long[n][n];
        for(int i = 0; i < n; i++) comb[i][0] = comb[i][i] = 1;

        for(int i = 2; i < n; i++) {
            for(int j = 1; j < i; j++) {
                comb[i][j] = (comb[i-1][j-1] + comb[i-1][j]) % MOD;
            }
        }
        return comb;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝羽飞鸟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值