leetcode必刷题 96.不同的二叉搜索树

本文介绍了如何使用动态规划解决给定整数n构成二叉搜索树的种数问题,通过分治策略和递推公式dp[i]=dp[j-1]*dp[i-j]计算不同节点数的树种数,最终给出时间复杂度为O(n^2)的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、问题描述:

给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。

二、解题思路:

二叉树是由根节点,左右子树组成的,二叉搜索树要满足左子树的所有元素都小于根节点,右子树的所有节点大于根节点。
这样就可以把一个问题划分为多个小问题(在根节点确定的时候,左右子树共有多少种情况),符合动态规划的思想。
假设共有 n 个节点,让 n 个节点依次轮流充当根节点,找到此时左右子树可能的排列情况,结果就是累加起来的和。

三、图解分析

  1. n=1 和 n=2 时:
    不难看出一个节点时,只有一种情况。
    两个节点时,以1为根节点,左子树为空。以2为根节点,右子树为空,共有两种情况。
    n=1和2时的情况
  2. 以此类推,n=3 时有:
    ①以1为根节点,左子树为空,右子树有两个节点,所以共有2种情况。
    ②以2为根节点,左子树有一个节点,右子树有一个节点,所以共有1种情况。
    ③以3为根节点,左子树有两个节点,右子树为空,所以共有2种情况。
    共有5种情况。
    n = 3时的五种情况

四、代码实现

//时间复杂度为 O(n^2)
class Solution {
    public int numTrees(int n) {
        int[] dp = new int[n + 1];
        dp[0] = 1;
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= i; j++){
                dp[i] += dp[i - j] * dp[j - 1];
            }
        }
        //for(int num : dp) System.out.println(num);//打印dp数组看一看与递推的结果是否一致
        return dp[n];
    }
}
  • 时间复杂度分析:外层循环需要遍历 n 次,内层循环每次需要遍历最多 i 次。因此,总体时间复杂度为 O(n^2)。

五、补充

  • 动态规划详解
    ①dp数组以及下标的含义:
    定义dp数组初始化长度为 n+1。dp[i] 表示 i 个节点可以组成 dp[i] 种二叉搜索树。
    ②递推公式:
    dp[i] += dp[j - 1] * dp[i - j];
    j 相当于是头结点的元素,从1遍历到 i 为止。
    j-1 为 j 为头结点左子树节点数量,i - j 为以 j 为头结点右子树节点数量。
    ③dp数组初始化
    dp[0] = 1。也可以加上dp[1] = 1;
    虽然题目写了n > 0,但是二叉树的左右子树可能是空的,dp[0]不能写成0,否则结果都变成0了,为空时也当成是一种排列方式。
    ④确定遍历顺序
    n 个节点的结果dp[n]要依赖 dp[1] ~ dp[i] 的值,所以先遍历 1 ~ n 里面每一个数作为头结点的状态,用 i 来遍历。
    根据递推公式dp[i] += dp[j - 1] * dp[i - j],节点数为 i 的 dp[i] 是依靠 i 之前节点数的状态。所以第二层遍历 1 ~ i 里面每一个数作为头结点的状态,用 j 来遍历。简单带入 n = 3的情况,递推公式的结果与图中的推论是一致的。
    ⑤核心代码如下:
for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= i; j++) {
        dp[i] += dp[j - 1] * dp[i - j];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值