算法-不同的二叉查找树I和II(动态规划和深搜算法)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法-不同的二叉查找树I和II(动态规划和深搜算法)相关的知识,希望对你有一定的参考价值。

二叉查找树在数据结构中学习,但是感觉自己学的非常水,最近在lintCode上做了两道的关于二叉查找树的题,感觉有比较记录下来,就当是增强记忆!

1.二叉查找树I

题意:

给出 n,问由 1...n 为节点组成的不同的二叉查找树有多少种?

样例:

给出n = 3,生成所有5种不同形态的二叉查找树:

1         3     3       2    1
 \       /     /       / \      3     2     1       1   3    2
 /     /       \                2     1         2                3

这个是数据结构中的二叉树中非常的常见。这个是典型卡特兰数的样例

  (1).卡特兰数

       令h(0)=1,h(1)=1,卡特兰数满足递归式:h(n) = h(0)*h(n-1) + h(1)*h(n-2) + ... + h(n-1)*h(0)  (n>=2);该递推公式为:h(n) = C(2n,n)/(n+1),n=0,1,2,3,... 

  (2).为什么二叉查找树满足卡特兰数呢?

     我们这样来假设

      当只有一个节点时,肯定只有一种情况,我们于是假设f(1) = 1;

     当只有两个节点时,怎么来考虑?我们固定一个节点,另一个节点要么放在左子树,要么放在右子树,所以分为两种情况,f(2) = f(1) + f(1)。其中的f(1),我们这样来理解,第一个f(1)实际上是f(1) * f(0)(这里的f(0)等于1,为什么f(0)等于1呢?因为没有节点也只有一种情况啊!),表示意思是,左子树只有一个节点,右子树没有节点,而第二个f(1)表示为f(0) * f(1),表示的是左子树没有节点,右子树只有一个节点。

    当只有三个或者三个节点以上时,我们假设为n, f(n)的情况就分为左子树为0并且右子树为n - 1(因为有一个节点被固定了,所以只有n - 1个节点),左子树为1并且右子树为n - 2,左子树为2并且右子树为 n - 3等等...然后我们就可以退出公式:f(n) = f(0) * f(n - 1) + f(1) * f(n - 2) + ...... + f(n - 2) * f(1) + f(n - 1) * f(1)。

  (3).动态规划

  根据上面的递归公式,我们可以写出动态规划的代码

    public int numTrees(int n) {
        
        int nums[] = new int[n + 1];
        nums[0] = 1;
        for(int i = 1; i <= n; i++) {
            for(int j = 0; j < i; j++) {
                nums[i] += nums[j] * nums[i - 1- j];
            }
        }
        return nums[n];
    } 

2.二叉查找树II

  题意:

给出n,生成所有由1...n为节点组成的不同的二叉查找树

样例:

给出n = 3,生成所有5种不同形态的二叉查找树:

1         3     3       2    1
 \       /     /       / \      3     2     1       1   3    2
 /     /       \                2     1         2                3

说实话,这道题真的不知道怎么写,标签上写的是深搜,但是就是不知道怎么写出来,因为之前只写过深搜的搜索题,没有做过深搜的生成类型题!

 最好我在网络上找的别人的代码,然后理解了,也不知道理解的是否正确。

 先来看看代码:

    public List<TreeNode> generateTrees(int n) {
        List<TreeNode> list = new ArrayList<>();
        if(n < 0) {
            return null;
        }
        list = createTree(1, n);
        return list;
    }

    private List<TreeNode> createTree(int start, int end) {
        List<TreeNode> list = new ArrayList<>();
        if (start > end) {
            list.add(null);
            return list;
        }
        for (int i = start; i <= end; i++) {
            List<TreeNode> left = createTree(start, i - 1);
            List<TreeNode> right = createTree(i + 1, end);
            for (int j = 0; j < left.size(); j++) {
                for (int k = 0; k < right.size(); k++) {
                    TreeNode node = new TreeNode(i);
                    node.left = left.get(j);
                    node.right = right.get(k);
                    list.add(node);
                }
            }
        }
        return list;
    }

我是这样理解的:比如n个节点,它有分为n - 1种情况,跟上面的卡特兰数一样,然后我们分别构造当前节点的左子树和右子树,通过递归来构造。由于有n - 1种情况,所以得有一个循环来构造。

 

以上是关于算法-不同的二叉查找树I和II(动态规划和深搜算法)的主要内容,如果未能解决你的问题,请参考以下文章

Leetcode练习(Python):动态规划类:第95题:不同的二叉搜索树 II:给定一个整数 n,生成所有由 1 ... n 为节点所组成的二叉搜索树。

Leetcode练习(Python):动态规划类:第95题:不同的二叉搜索树 II:给定一个整数 n,生成所有由 1 ... n 为节点所组成的二叉搜索树。

96. 不同的二叉搜索树

代码随想录算法训练营第四十一天| 343. 整数拆分 96.不同的二叉搜索树

LintCode刷题——不同的二叉查找树III

95. 不同的二叉搜索树 II