java数据结构与算法刷题-----LeetCode95. 不同的二叉搜索树 II

java数据结构与算法刷题目录(剑指Offer、LeetCode、ACM)-----主目录-----持续更新(进不去说明我没写完):https://blog.csdn.net/grd_java/article/details/123063846

在这里插入图片描述

分治回溯+记忆化搜索

卡特兰数,例如对于n个进栈元素,有多少种出栈顺序,就可以用卡特兰数来求,公式为 1 n + 1 C n m = 1 n + 1 A n m A m m \dfrac{1}{n+1}C_{n}^m =\dfrac{1}{n+1} \dfrac{A_{n}^m}{A_{m}^m} n+11Cnm=n+11AmmAnm,例如n = 3时,有 1 4 ∗ 6 ∗ 5 ∗ 4 3 ∗ 2 ∗ 1 = 5 \dfrac{1}{4}*\dfrac{6*5*4}{3*2*1} = 5 41321654=5种出栈顺序

  1. A n m = A_{n}^m = Anm=从n开始从大往小,m个数相乘
  2. C n m = A n m A m m C_{n}^m = \dfrac{A_{n}^m}{A_{m}^m} Cnm=AmmAnm
  3. 例如 C 2 n n = A 2 n m A n n C_{2n}^n = \dfrac{A_{2n}^m}{A_{n}^n} C2nn=AnnA2nm
  4. 例如n = 3时, C 2 n n = C 6 3 = A 6 3 A 3 3 = 6 ∗ 5 ∗ 4 3 ∗ 2 ∗ 1 = 20 C_{2n}^n = C_{6}^3 = \dfrac{A_{6}^3}{A_{3}^3} = \dfrac{6*5*4}{3*2*1} = 20 C2nn=C63=A33A63=321654=20
解题思路:时间复杂度O( n ∗ n 的卡特兰数 n*n的卡特兰数 nn的卡特兰数),对每一个结点作为根结点的枚举,都需要n的卡特兰数的时间复杂度,因为它的过程和不同出栈顺序一样。空间复杂度O( n ∗ n 的卡特兰数 n*n的卡特兰数 nn的卡特兰数)
  1. 先通过分治来划分回溯区域,例如1,2,3, 先让1来分治,然后1和2来分治,然后2和3来分治,最后1,2,3来分治
  2. 分治划分区域后,开始回溯枚举,枚举用不同数字来当根结点的划分方式

并以当前根结点为中心,左右划分两个区域继续分治

  1. 因为是升序需要,当我们选择一个数字作为根结点,其左边比它小的都在左子树,比它大的都在右子树。具体可以参考96题,因为这道题是96题的衍生题
🏆LeetCode96. 不同的二叉搜索树https://blog.csdn.net/grd_java/article/details/135616416
代码

在这里插入图片描述

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public List<TreeNode> generateTrees(int n) {
        if (n == 0) return Collections.emptyList();
        List<TreeNode>[][] memory = new ArrayList[n][n];//记忆化搜索
        return generateTrees(1, n, memory);//分治,回溯
    }
    private List<TreeNode> generateTrees(int left, int right, List<TreeNode>[][] memory) {
        List<TreeNode> ans = new ArrayList<>();
        // if (left > right) return new ArrayList<TreeNode>(){{add(null);}}; 下面代码的简写形式
        if (left > right) {ans.add(null); return ans;}
        //如果当前left和right相同,创建这个结点
        if (left == right){ans.add(new TreeNode(left)); return ans;}
        //记忆化搜索,如果已经处理过,就直接返回
        List<TreeNode> treeNodes = memory[left - 1][right - 1];
        if (treeNodes != null) return treeNodes;
        //没处理过就处理
        for (int i = left; i <= right; i++) {//以i为根结点,其左边分治为左子树,右边分治为右子树
            List<TreeNode> leftTrees = generateTrees(left, i - 1, memory);//获取i的左子树
            List<TreeNode> rightTrees = generateTrees(i + 1, right, memory);//获取i的右子树
            for (TreeNode leftTree : leftTrees) {
                for (TreeNode rightTree : rightTrees) {
                    ans.add(new TreeNode(i,leftTree,rightTree));//以i为根结点构建搜索树
                }
            }
        }
        memory[left - 1][right - 1] = ans;//记忆
        return ans;
    }
    
}
  • 34
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

殷丿grd_志鹏

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

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

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

打赏作者

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

抵扣说明:

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

余额充值