相信很多小伙伴刷题的时候面对力扣上近两千到题目,感觉无从下手,我花费半年时间整理的Github学习项目:leetcode刷题指南,不仅有详细经典题目刷题顺序而且对应题解来排好了,难点还有视频讲解,按照list一道一道刷就可以了,给个star支持一下吧!

96.不同的二叉搜索树

题目链接:https://leetcode-cn.com/problems/unique-binary-search-trees/

给定一个整数 n,求以 1 … n 为节点组成的二叉搜索树有多少种?

示例:

思路

这道题目描述很简短,但估计大部分同学看完都是懵懵的状态,这得怎么统计呢?

关于什么是二叉搜索树,我们之前在讲解二叉树专题的时候已经详细讲解过了,也可以看看这篇二叉树:二叉搜索树登场!在回顾一波。

了解了二叉搜索树之后,我们应该先举几个例子,画画图,看看有没有什么规律,如图:

n为1的时候有一棵树,n为2有两棵树,这个是很直观的。

来看看n为3的时候,有哪几种情况。

当1为头结点的时候,其右子树有两个节点,看这两个节点的布局,是不是和 n 为2的时候两棵树的布局是一样的啊!

(可能有同学问了,这布局不一样啊,节点数值都不一样。别忘了我们就是求不同树的数量,并不用把搜索树都列出来,所以不用关心其具体数值的差异)

当3为头结点的时候,其左子树有两个节点,看这两个节点的布局,是不是和n为2的时候两棵树的布局也是一样的啊!

当2位头结点的时候,其左右子树都只有一个节点,布局是不是和n为1的时候只有一棵树的布局也是一样的啊!

发现到这里,其实我们就找到的重叠子问题了,其实也就是发现可以通过dp[1] 和 dp[2] 来推导出来dp[3]的某种方式。

思考到这里,这道题目就有眉目了。

dp[3],就是 元素1为头结点搜索树的数量 + 元素2为头结点搜索树的数量 + 元素3为头结点搜索树的数量

元素1为头结点搜索树的数量 = 右子树有2个元素的搜索树数量 * 左子树有0个元素的搜索树数量

元素2为头结点搜索树的数量 = 右子树有1个元素的搜索树数量 * 左子树有1个元素的搜索树数量

元素3为头结点搜索树的数量 = 右子树有0个元素的搜索树数量 * 左子树有2个元素的搜索树数量

有2个元素的搜索树数量就是dp[2]。

有1个元素的搜索树数量就是dp[1]。

有0个元素的搜索树数量就是dp[0]。

所以dp[3] = dp[2] * dp[0] + dp[1] * dp[1] + dp[0] * dp[2]

如图所示:

此时我们已经找到的递推关系了,那么可以用动规五部曲在系统分析一遍。

  1. 确定dp数组(dp table)以及下标的含义

dp[i] : 1到i为节点组成的二叉搜索树的个数为dp[i]

也可以理解是i的不同元素节点组成的二叉搜索树的个数为dp[i] ,都是一样的。

以下分析如果想不清楚,就来回想一下dp[i]的定义

  1. 确定递推公式

在上面的分析中,其实已经看出其递推关系, dp[i] += dp[以j为头结点左子树节点数量] * dp[以j为头结点右子树节点数量]

j相当于是头结点的元素,从1遍历到i为止。

所以递推公式:dp[i] += dp[j - 1] * dp[i - j]; ,j-1 为j为头结点左子树节点数量,i-j 为以j为头结点右子树节点数量

  1. dp数组如何初始化

初始化,只需要初始化dp[0]就可以了,推导的基础,都是dp[0]。

那么dp[0]应该是多少呢?

从定义上来讲,空节点也是一颗二叉树,也是一颗二叉搜索树,这是可以说得通的。

从递归公式上来讲,dp[以j为头结点左子树节点数量] * dp[以j为头结点右子树节点数量] 中以j为头结点左子树节点数量为0,也需要dp[以j为头结点左子树节点数量] = 1, 否则乘法的结果就都变成0了。

所以初始化dp[0] = 1

  1. 确定遍历顺序

首先一定是遍历节点数,从递归公式:dp[i] += dp[j - 1] * dp[i - j]可以看出,节点数为i的状态是依靠 i之前节点数的状态。

那么遍历i里面每一个数作为头结点的状态,用j来遍历。

代码如下:

for (int i = 1; i <= n; i++) {for (int j = 1; j <= i; j++) {dp[i] += dp[j - 1] * dp[i - j];}
}
  1. 举例推导dp数组

n为5时候的dp数组状态如图:

当然如果自己画图举例的话,基本举例到n为3就可以了,n为4的时候,画图已经比较麻烦了。

我这里列到了n为5的情况,是为了方便大家 debug代码的时候,把dp数组打出来,看看哪里有问题

综上分析完毕,C++代码如下:

class Solution {
public:int numTrees(int n) {vector<int> dp(n + 1);dp[0] = 1;for (int i = 1; i <= n; i++) {for (int j = 1; j <= i; j++) {dp[i] += dp[j - 1] * dp[i - j];}}return dp[n];}
};
  • 时间复杂度O(n^2)
  • 空间复杂度O(n)

大家应该发现了,我们分析了这么多,最后代码却如此简单!

总结

这道题目虽然在力扣上标记是中等难度,但可以算是困难了!

首先这道题想到用动规的方法来解决,就不太好想,需要举例,画图,分析,才能找到递推的关系。

然后难点就是确定递推公式了,如果把递推公式想清楚了,遍历顺序和初始化,就是自然而然的事情了。

可以看出我依然还是用动规五部曲来进行分析,会把题目的方方面面都覆盖到!

而且具体这五部分析是我自己平时总结的经验,找不出来第二个的,可能过一阵子 其他题解也会有动规五部曲了,哈哈

当时我在用动规五部曲讲解斐波那契的时候,一些录友和我反应,感觉讲复杂了。

其实当时我一直强调简单题是用来练习方法论的,并不能因为简单我就代码一甩,简单解释一下就完事了。

可能当时一些同学不理解,现在大家应该感受方法论的重要性了,加油

「代码随想录」96.不同的二叉搜索树【动态规划】详解!相关推荐

  1. AVL树(平衡二叉搜索树)详解及C++代码实现

    AVL树简介 AVL树实际上一个引入了平衡因子的二叉搜索树,该平衡因子保证了每个节点的左右子树高度之差的绝对值不超过1,这样就可以降低树的高度,减少平均搜索长度. 一棵AVL树或者是空树,或者是具有以 ...

  2. 二叉搜索树(BST)详解

    数据结构:二叉搜索树(BST) 今天咱们来聊聊二叉搜索树,先从字面上来理解,二叉,指的是有两个分支,左子树和右子树:搜索树,啥意思呢,搜索,是不是就是之前学过dfs和bfs那边学过,对啊,dfs就是深 ...

  3. 89. Leetcode 96. 不同的二叉搜索树 (动态规划-基础题)

    步骤一.确定状态: 确定dp数组及含义 dp数组的长度是n+1, dp[i]表示的是从1...i为节点组成的二叉搜 索树的个数 步骤二.推断状态方程: 步骤三.规定初始条件: 初始条件: dp[0]= ...

  4. LeetCode 96不同的二叉搜索树95不同的二叉搜索树Ⅱ

    微信搜一搜:bigsai 算法文章题解全部收录在github仓库bigsai-algorithm 关注回复进群即可加入力扣打卡群,欢迎划水.近期打卡: LeetCode 92反转链表Ⅱ&93复 ...

  5. leetcode - 96. 不同的二叉搜索树

    96. 不同的二叉搜索树 ------------------------------------------ 给定一个整数 n,求以 1 - n 为节点组成的二叉搜索树有多少种? 示例: 输入: 3 ...

  6. Leetcode 96. 不同的二叉搜索树

    Leetcode 96. 不同的二叉搜索树 1.问题分析 2.问题解决 3.总结 1.问题分析 题目链接:https://leetcode-cn.com/problems/unique-binary- ...

  7. 《dp补卡——343. 整数拆分、96. 不同的二叉搜索树》

    343. 整数拆分 1.确定dp数组以及下标含义. dp[i]:分拆数字i,可以得到的最大的乘积 2.确定递推公式: dp[i]最大乘积出处:从1遍历j到i,j * dp[i-j] 与 j * (i- ...

  8. LeetCode 96——不同的二叉搜索树

    1. 题目 2. 解答 以 \(1, 2, \cdots, n\) 构建二叉搜索树,其中,任意数字都可以作为根节点来构建二叉搜索树.当我们将某一个数字作为根节点后,其左边数据将构建为左子树,右边数据将 ...

  9. 【LeetCode笔记】96. 不同的二叉搜索树(Java、动态规划)

    文章目录 题目描述 代码 & 思路 精简版 2.0 题目描述 这道题其实不用构造数据结构 二叉搜索树:只要利用这个结构的性质即可,即:左右两子,左小右大 然后用动态规划来做,具体如何推导见思路 ...

  10. LeetCode 96. 不同的二叉搜索树(Unique Binary Search Trees )

    题目描述 给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种? 示例: 输入: 3 输出: 5 解释: 给定 n = 3, 一共有 5 种不同结构的二叉搜索树:1 3 3 2 1\ ...

最新文章

  1. 关于MYSQL 字符转义问题总结
  2. FPGA底层资源介绍
  3. SAX解析XML文件
  4. Comet oj比赛组队
  5. Android 操作系统为什么不启用swap?
  6. Arts 第十八周(7/15 ~ 7/21)
  7. 6个炫酷又好用的 Python 工具,个个都很奔放呀
  8. STC15W408读取HX711称重数据串口发送
  9. Lync Server 2013 安装准备工具 for Win 2008 R2
  10. ThinkPHP5中的助手函数
  11. canvas放射粒子效果
  12. python set和frozenset 异同点学习记录
  13. Selective Search for Object Recoginition(转)
  14. 第五章 编码/加密——《跟我学Shiro》[张开涛]
  15. 去除WINRAR的广告
  16. cmd命令删除计算机密码,怎么用DOS命令查询或消除电脑登陆密码?
  17. z世代消费力白皮书_LSPACE丨Z世代虽穷但买的态度你真的懂吗
  18. 图像特征原理--HOG特征
  19. 51单片机串口通信控制LED(hex)
  20. 教师python培训心得体会

热门文章

  1. 数据比较1.0(文本格式)
  2. iOS------自动查找项目中不用的图片资源
  3. Java war包取之外的properties文件
  4. sqlserver shiwu
  5. python解压.tar.gz
  6. 章节十五、6-log4 2-用默认的配置
  7. python基础:re模块匹配时贪婪和非贪婪模式
  8. Python学习 :文件操作
  9. winform textbox提示历史记录
  10. Struts2---ActionContext和ServletActionContext小结