LeetCode每日一题(不同的二叉搜索树)

不同的二叉搜索树

题目地址:https://leetcode-cn.com/problems/unique-binary-search-trees/
题目大意:给定一个整数 n,求以 1 … n 为节点组成的二叉搜索树有多少种?

分析:
一般看到求有多少种而不要求列出所有排列的题目,我们都会自然的想到使用动态规划。
不过这道题的动态规划还是有一点难度的,想到了之后会觉得非常简单,但是没有想到的话就感觉无从下手。

对于一颗二叉搜索树 而言,最重要的就是根节点,根据根节点的不同,我们可以把这颗二叉搜索树分为左子树和右子树。而左子树和右子树分别也满足二叉搜索树 的条件,所以我们可以继续划分下去,知道只有一个节点或者是节点为空。

由此可以得出状态转移方程,i为当前枚举的根节点,n为当前总节点数量,顺便一说,这个公式就是著名的卡特兰数:

d p ( n ) = i = 1 n d p ( i 1 ) d p ( n i ) dp(n) = \sum_{i=1}^{n}dp(i-1)\cdot dp(n-i)
这样就不难得出代码:

class Solution {
    public int numTrees(int n) {
        if(n <= 1){
            return 1;
        }
        int[] dp = new int[n+1];
        dp[0] = 1;
        dp[1] = 1;
        //枚举之前的n种情况
        for(int i=2;i<n+1;i++){
            //枚举所有可能的根节点
            for(int j=1;j<=i;j++){
            	//以j为根节点进行划分,
            	//左边依赖于dp[j-1]
            	//右边依赖于dp[i-j]
                dp[i] += dp[j-1]*dp[i-j];
            }
        }
        return dp[n];
    }
}

不同的二叉搜索树 Ⅱ

题目地址:https://leetcode-cn.com/problems/unique-binary-search-trees-ii/
题目大意:给定一个整数 n,生成所有由 1 … n 为节点所组成的 二叉搜索树 。
分析:
总的来说这道题就是上面那道题的升级版,要求列出所有的可能。
一般看到这种全排列的题,我们会想到回溯法,但可惜的是这道题用回溯法做起来比较麻烦。我们采用另外一种用于全排列的方法——递归。

刚才的动态规划是一种自底向上的划分,而递归则是从顶向下的划分,我们把一个问题拆分成若干个和原来相似的小问题。
对于这道题来说,我们刚才分析过了首先需要枚举根节点,枚举根节点之后,我们可以把序列(1,n)划分为两个子集(1,i-1) (i+1,n),之后继续划分左右两个子集,直到子集为空,就开始返回。
我们先以一个元素的集合开始考虑,一个元素两个子集为空,,这时候只有一种情况,所以我们将这个节点的左右指针设置为null之后返回。
再上一级,集合中有三个元素,可以划分出三个不同的子集,以(1,2,3)为例,我们可以划分为(1),(2,3)也可以是(1),(2),(3)(1,2),(3),对于第一种情况,根节点的左子树为null,但是右子树有两个选择,所以我们需要构造出两种情况的根节点并且放入结果集合中。这样最后集合中就有了五种树。
再往上一层,假如有集合(1,2,3,4),当以4为根节点的时候,可以划分为(1,2,3)(4)(1,2,3)的情况我们讨论过了,是一个有五种树的集合,那么我们根据这五种左子树的不同,又可以构建出新的二叉树。

所以总的来说代码如下:

class Solution {
    public List<TreeNode> generateTrees(int n) {
        if (n == 0) {
            return new LinkedList<TreeNode>();
        }
        return generateTrees(1, n);
    }

    public List<TreeNode> generateTrees(int start, int end) {
        List<TreeNode> allTrees = new LinkedList<TreeNode>();
        if (start > end) {
            allTrees.add(null);
            return allTrees;
        }

        // 枚举可行根节点
        for (int i = start; i <= end; i++) {
            // 获得所有可行的左子树集合
            List<TreeNode> leftTrees = generateTrees(start, i - 1);

            // 获得所有可行的右子树集合
            List<TreeNode> rightTrees = generateTrees(i + 1, end);

            // 从左子树集合中选出一棵左子树,从右子树集合中选出一棵右子树,拼接到根节点上
            for (TreeNode left : leftTrees) {
                for (TreeNode right : rightTrees) {
                    TreeNode currTree = new TreeNode(i);
                    currTree.left = left;
                    currTree.right = right;
                    allTrees.add(currTree);
                }
            }
        }
        return allTrees;
    }
}

//作者:LeetCode-Solution
//链接:https://leetcode-cn.com/problems/unique-binary-search-trees-//ii/solution/bu-tong-de-er-cha-sou-suo-shu-ii-by-leetcode-solut/
//来源:力扣(LeetCode)
//著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

猜你喜欢

转载自blog.csdn.net/qq_33241802/article/details/107480804