【LeetCode】 96. Unique Binary Search Trees

本文详细解析了如何使用动态规划方法计算不同形态的二叉搜索树数量,通过实例演示了状态转移方程的推导过程,适用于算法理解和面试准备。

分析问题

分析题意,二叉搜索树的不同的原因在于数组中所有的元素均可以成为祖先节点,因此

  • 第一步: 遍历数组,使得每一个元素都可以成为祖先节点。
  • 第二步,确定好祖先节点之后,数组中的剩余n-1个数就会被分成两拨,左子树由小于祖先节点的数组成,有节点由大于祖先节点的数组成

当祖先节点值为 iii 时,左子树最大的数为 i−1i -1i1, 于是左子树的元素个数可以从0增加到 i−1i - 1i1

例如: 当n=3是 num = [1, 2, 3]
此时根节点有三种情况:

  • 1为根节点时,1的左子树是0个,右子树是2,3(两个)。所以1是根节点的种类树是: res[0] * res[2]
  • 2为根节点时,2的左子树是1(1个),右子树是3(1个)。所以2是根节点的种类树是: res[1] * res[1]
  • 3为根节点时,3的左子树是1,2(2个),右子树是0个。所以3是根节点的种类树是: res[2] * res[0]
    所以 res[3] = res[0]*res[2] + res[1]*res[1] + res[2]*res[0]

确认状态

dp[0]=1dp[0] = 1dp[0]=1: 没有节点只有一种情况,空树
dp[1]=dp[0]∗dp[0]=1dp[1] = dp[0] * dp[0] = 1dp[1]=dp[0]dp[0]=1:只有一个节点也是一种情况
假设n=3时:

iiidp[i]dp[i]dp[i]
i=1i = 1i=1dp[1]=dp[0]∗dp[0]dp[1] = dp[0] * dp[0]dp[1]=dp[0]dp[0]1
i=2i = 2i=2dp[2]=dp[0]∗dp[1]dp[2] = dp[0] * dp[1]dp[2]=dp[0]dp[1]dp[2]=dp[1]∗dp[0]dp[2] = dp[1] * dp[0]dp[2]=dp[1]dp[0]2
i=3i = 3i=3dp[3]=dp[0]∗dp[2]dp[3] = dp[0] * dp[2]dp[3]=dp[0]dp[2]dp[3]=dp[1]∗dp[1]dp[3] = dp[1] * dp[1]dp[3]=dp[1]dp[1]dp[3]=dp[2]∗dp[0]dp[3] = dp[2] * dp[0]dp[3]=dp[2]dp[0]5

问题公式化:

dp(n)={1,n=0 ∑i=1ndp(i−1)dp(n−i),if n≥1  dp(n)= \begin{cases} 1, & \text {$n=0$ } \\ \sum_{i=1}^{n} dp(i-1)dp(n-i), & \text{if $n \geq 1$ } \end{cases} dp(n)={1,i=1ndp(i1)dp(ni),n=if n

// 参考程序
 class Solution:
    def numTrees(self, n):
        """
        :type n: int
        :rtype: int
        """
        if n == 0 or n == 1:
            return 1
        dp = [0] * (n+1)
		
        dp[0] = 1    // 初始状态
        for i in range(1, n+1):
            for j in range(i):
                dp[i] += dp[j] * dp[i-j-1]       

        return dp[n]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值