小白日更第十七天->力扣第96题(不同的二叉搜索树)

先看下题目描述吧在这里插入图片描述

这道题目说真的,因为是我在做题之前选的就是动态规划的题目。所以我一直在往动态规划上想。

最初的想法

那我第一步想的是最终状态的上一步应该是什么样子,因为动态规划的问题有一个步骤就是确定状态。
想了好久没想出来,然后我就像那子问题是什么,给的整数n是3的时候和n等于1和n等于2的时候有什么关系,也画了好一会,发现出了一些规律,比如我们很容易可以画出n=1和n=2时候的情况,那其实n=3的时候就是在n=2的基础上把3分别插在根节点和插在叶子节点在加上插在中间,这三种情况的和就是n=4时候的情况,我们也很快就画出了n=4的情况,其实也找到了规律,首先n=4的时候一共有14种,那当n=5的时候插在头部和插在尾部一共有2*14=28种,加上插在中间的情况,插在中间必须是像12,13,14,23,24,34这种,前面的数大后面的数小,这样才可以把55插进去,否则5可能就又成为了整棵树的根节点或者又成为了叶子节点,那样就重复计算了,这样我们把插在中间的情况加进去一共是28+6=34种。答案是42种,少了8种,这8种其实也可以想到,比如当5插入到2和3的中间,那这个时候5是2的右子树,3是5的左子树。而这个时候4是3的右子树,这个时候在5位置不变的前提下,4也可以直接当作5的右子树,这就又是一种情况,所以我们就是在5位置不变的前提下又可以组合出好几种排列。最后实在想不出了,看了看官方的回答。

官方解释

我看了下官方给这道题的解释,一语惊醒梦中人,思路是这样的,比如说当我们的n等于4的时候,我们从1开始遍历比如遍历到k,k依次当作根节点,然后这样**1 -(k-1)都在k的左边,(k+1 - n)**都在k的右边,这样左右两边在分别做这样的操作,这样我们其实就已经完成了50%的工作,下一步就是动态方程,我在官方那copy过来的,大家看一下

1、G(n): 长度为 n 的序列能构成的不同二叉搜索树的个数。
2、F(i,n): 以 i为根、序列长度为 n 的不同二叉搜索树个数 (1≤i≤n)。

那G(n)就是我们最终想要得到的那个结果,我们再求G(n)的时候是要依赖于F(i,n)的,而F(i,n)又依赖于G(n),比如n=5,现在3当作根节点,那1和2都在3的左边,4和5都在3的右边,1和2可以根据G(2)我们已经求得的子问题来直接获取,因为3左边的一定是比3小的并且是连续的比如1,2,右边同样也是G(2),这就很容易得出当n=5,3当作根节点的情况就是G(2)*G(2),然后就是for循环,累加就可以了。我们看代码

class Solution {
    public int numTrees(int n) {
        int[] G = new int[n + 1];
        //初始化状态
        G[0] = 1;
        G[1] = 1;
        
        for (int i = 2; i <= n; ++i) {
            for (int j = 1; j <= i; ++j) {
                G[i] += G[j - 1] * G[i - j];
            }
        }
        return G[n];
    }
}

这里的for循环,我们还是以n=5为例,j从1开始也就相当于从1开始当作根节点,那1的左子树自然为0也就是G[j-1],而右边是G[i-j]这时候i=5,那G[i-j]也就是G[4],上一步我们已经算出G[4],所以这种重复子问题直接拿来就用。然后依次当作根节点,也就是根节点的左子树从0到4,而根节点的右子树从4到0。突然感觉官方给的这个代码是不是可以优化一下啊,因为这是对称的操作。算了感兴趣的小伙伴可以自己尝试一下,写完这篇博客,我也思考一下这个问题。

以上是我根据官方的解释对这道题理解的感觉还算透彻,但是在想动态方程的时候感觉好难啊~还是需要加强练习,希望小伙伴们可以在评论区讨论一下动态规划问题的心得体会,大家一起分享一下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值