算法数据结构和算法分析算法提高之LeetCode刷题

1569-将子数组重新排序得到同一个二叉查找树的方案数

2020-09-04  本文已影响0人  华雨欣

题目

核心思路

这道题最主要的就是理解二叉搜索树(BST)的插入原理,然后就要通过数学计算排列组合可能了,直接上图解分析即可。


根据二叉搜索树(BST)的定义,以及插入过程,可以发现,第一个元素作为根节点,必须是确定不变的,又因为二叉搜索树规定了其左边元素均小于它本身,它右边元素均大于它本身,也就是说,确定根节点后,左子树有哪些结点、右子树有哪些结点都确定了,既然如此,我们可以将除了根节点以外的结点分为:左子树的结点与右子树的结点 两类,这样两类结点就相互独立互不影响了,左子树和右子树之间的顺序可以任意交叉(PS:如果来了一个结点属于左子树,那么他肯定不会插入到右子树,故不会相互影响 )
所以,所有的可能种数就是:左右子树间的排列 * 左子树的可能性 * 右子树的可能性,而 左子树的可能性右子树的可能性 恰好是原问题的子问题,所以把他交给递归处理即可,需要解决的就是 左右子树间的排列

参考上例,除了根节点剩余4个结点,左右子树间互不影响所以可以把左子树看成L结点,右子树看成R结点,他们的排列也就是总共四个位置,选出2个位置来放L结点,剩下2个位置来放R结点即可:C₄² = 6 (也就是相当于下标为n - 1,上标为比根节点小的结点个数)
综上,只要我们提前求出范围内的组合数(使用杨辉三角递推),然后递归计算即可。

完整代码

class Solution {

    private int[][] C;// 存储组合数
    private final int MOD = (int) 1e9 + 7;

    public int numOfWays(int[] nums) {
        int n = nums.length;
        C = new int[n][n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j <= i; j++) {
                if (j == 0) {
                    C[i][j] = 1;
                } else {
                    C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % MOD;
                }
            }
        }

        return (f(nums) + MOD - 1) % MOD;
    }

    private int f(int[] nums) {
        if (nums == null || nums.length == 0)
            return 1;
        int n = nums.length;
        int k = nums[0];
        int[] left = new int[k - 1], right = new int[n - k];// 左右子数组
        int li = 0, ri = 0;// 左右数组下标
        for (int i = 1; i < n; i++) {
            if (nums[i] > k) {
                right[ri++] = nums[i] - k;// 将数据按原大小和顺序与下标对齐
            } else {
                left[li++] = nums[i];
            }
        }

        return (int) ((long) C[n - 1][k - 1] * f(left) % MOD * f(right) % MOD);
    }
}

这里有三个需要注意的细节

总结

又是一道数学题,唉,高中我还挺喜欢数学的,可上了大学就越来越不行了,做题时候也是好多一般性的问题找不到突破点,只能一点点学习学习这样的思路了。
如果文章有写的不正确的地方还请指出。感恩相遇~~~

上一篇下一篇

猜你喜欢

热点阅读