leetcode 96. 不同的二叉搜索树 (暴力搜索->动态规划)

题目:

在这里插入图片描述

解题思路

  • 阅读左神算法书动态规划章节“机器人行走的步数”一题有感,我们对这类问题可以从暴力解法逐步找到动态规划的解法

  • 首先,序列1,2,…,n是一个升序序列,可以认为是一棵搜索二叉树的中序序列,因此:

    • 我们迭代的把每个数当作是根节点,求它的左右子树分别有几种组合,相乘即可,比如N为5,根节点为3时,求左子树[1,2],右子树[4,5]的组合数相乘,叠加到最后结果
    • 当然如果一个子树为空,当前组合数就是另一棵子树的组合数
    • 这是一个递归过程,写出如下代码:
    def numTreesEnum(self, n: int) -> int:
        def process(start, end):
            # 如果当前树为空
            if start >= end:
                return 1
            res = 0
            for i in range(start, end+1):
                res += process(start, i-1) * process(i+1, end)
            return res
        return process(1, n)
    
  • 以上的代码时间复杂度是O(n!), 因为对子问题作了很多重复计算:当N为5时,假设根节点为1,我们的计算里包含了子树[3,4,5],根节点为2时,计算里也包含了子树[3,4,5]…

  • 经过分析我们发现,上述算法的过程是以“哪个数是根节点”作为迭代基础,求算节点数量为end-start+1的子树长度并作累积,我们不妨以“根节点”,“树的节点数量”两个变量为研究对象,构造出一个dp矩阵,

  • 那么当n=4时,如果行对应“根节点”,列对应“树的节点数量”,这个矩阵就是这样的:
    image.png

  • 如果我们求出了最后一列的值,这一列求和就是最终答案!

  • Base Case是什么?

  • 当节点数量为1时,根节点也为1,只有1种情况,填入1;根节点为2,3,4时,这些树不存在,所以都是0,所以矩阵变成这样:
    image.png

  • 然后再来看节点数量为2的时候:

    • 根节点为1时,左子树0个节点,右子树1个节点,共1种情况;
    • 根节点为2时,右子树0个节点,左子树1个节点,共1种情况;
    • 其他情况都是0
  • 然后再来看节点数量为3的时候:

    • 根节点为1时,左子树0个节点,右子树2个节点(因为sum(因为第2列的和是2),共2种情况;
    • 根节点为2时,左子树1个节点(因为第1列的和是1),右子树1个节点(因为第1列的和是1),共1种情况;
    • 根节点为3时,左子树2个节点(因为第2列的和是2),右子树0个节点,共2种情况
  • 由上面分析得出,对每一种情况,只需求出左子树数量L,右子树数量R,使当前情况对应的格子等于第L-1列的和乘上第R-1列的和即可

  • 最后的矩阵:
    image.png

  • 我们对每一列的计算,只需要用到之前列的和,所以我们不需要nxn的dp矩阵,只需要长度为n的dp数组,

  • 最后算法时间复杂度是O(n^2), 空间复杂度是O(n),代码如下:

代码

class Solution:
    def numTrees(self, n: int) -> int:
        res = [0] * (n + 1)
        res[1] = 1
        for j in range(2, n+1):
            curr_sum = 0
            for i in range(1, n+1):
                if i > j:
                    break
                left = i - 1
                right = j - i
                left = 1 if left == 0 else left
                right = 1 if right == 0 else right
                curr_sum += res[left] * res[right]
            res[j] = curr_sum
        return res[n]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值