算法学习笔记——生成搜索二叉树
这是LeetCode第95、96题
不同二叉搜索树的种类
首先是研究二叉搜索树的数量性质:
给你一个整数
n
,求恰由n
个节点组成且节点值从1
到n
互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。
二叉搜索树,指对于根节点,左子树所有元素均小于该节点,右子树所有节点都大于这个节点。
例如,当n=1,显然只有一种情况。而当n=2,就有2种情况,这是因为1、2都可以作为根节点,产生2个不同的树。
特别地,当n=0也是一种情况,我们把NULL也看作一棵树。
接下来考虑n=3,它的根节点可以是1、2、3。从二叉搜索树性质出发:
- 当根节点为1,它不会有左子树,而右子树有两个节点。
- 当根节点为2,它一定有左右子树,而且各有一个节点。
- 当根节点为3,情况与1类似。
这样,就找到了一个子问题。例如,当根节点为1,它的子问题就是:没有节点的二叉搜索树有几种情况,有2个节点的二叉搜索树有几种情况。
推广到一般情况并以此列出方程:
$$
dp[i] = \sum_{j=0}^{i-1}dp[j]dp[i-j-1]
$$
1 | class Solution { |
列出所有不同的二叉搜索树
列出具体的二叉树,它们的头节点构成了一个vector。
1 | # Definition for a binary tree node. |
采用区间表示:定义$(left, right)$ 表示一棵起于$left$,终于$right$的二叉搜索树。
此时类似前一题的问题:头节点下接两棵子树,显然,对于一个根节点值为$i$的树,左边应该是由$[left, i-1]$构成的,而右边是由$[i+1, right]$构成的。
思考:递推关系和返回值应该是什么?
假定有一个函数$dfs(left,right)$用于构造如上述的树,那么在$left = right$时,它应该是一个节点;在$left>right$时,它应该是空的。在$left<right$时,它是一棵树.
*因为是一棵树,所以返回类型应该是TreeNode**吗?
再回到构造过程以及题目,题目要求返回的是一个vector<TreeNode*>
,它代表着树的集合。
而子问题:左右子树,它们同样是树的集合。
对于一个节点,它的左右子树都是一个集合,代表着不同情况的所有树,因此返回类型也应该是vector<TreeNode*>
。特别地,对于$left=right$,它是一个只有一个节点的树,当$left>right$,它是一棵空的树。
值得一提的是,对于传入的left、right是区间,枚举其根节点(自身)还需要使用一个循环。
因此,构造的函数:
1 | vector<TreeNode*> dfs(int left, int right); |
边界条件:
1 | if(left > right){ |
对于不同的根节点构造左右子树:
1 | vector<TreeNode*> res; // result |
遍历左右子树集合中的所有树,并保存在res中,注:这一段在循环内:
1 | for(auto &l: ltree){ |
再返回res即可。完整代码如下:
1 | vector<TreeNode*> dfs(int left, int right){ |
关于C++为什么应该使用nullptr而不是NULL,是因为在C语言中,NULL是可以被强制类型转换的。