美文网首页算法数据结构和算法分析算法提高之LeetCode刷题
1569-将子数组重新排序得到同一个二叉查找树的方案数

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

作者: 华雨欣 | 来源:发表于2020-09-04 20:49 被阅读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);
        }
    }
    

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

    • 在给定范围内,组合数是可能超过数据范围的,所以可以提前使它对MOD取模,保证数据在int范围内
    • 在使用组合数的时候,第一次乘上f(left也有可能越界,所以事先强转为long保证数据的正确性。
    • 由于使用组合数C[n - 1][k - 1]时,比k小的数选用 k - 1 ,也就是认为每个nums都是从1到n的,所以可以在更新右子树数组时将元素与下标对齐(nums[i] - k)
      剩下的内容就是交给递归,靠递归来分割左子树和右子树计算结果即可。

    总结

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

    相关文章

      网友评论

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

        本文链接:https://www.haomeiwen.com/subject/hbbwsktx.html