不同的二叉搜索树 II 图解题解
这道题到底在问什么
- 输入
- n=3
- 输出
- 5 棵(卡特兰数 C₃)
- 输入
- n=1
- 输出
- 1 棵(单节点)
最优解:一步一步想明白
- 3记住「每个值轮流当根 → 左区间全部形态 × 右区间全部形态」,下面对 n=3 逐棵套它。
- 4中序 = 升序先看一棵具体的 BST 找感觉:根 2、左 1、右 3。BST 要求左子树都比根小、右子树都比根大,等价于中序遍历严格升序 1、2、3。下面要找的,就是 n=3 时所有满足这一点的不同结构。
- 5n=3 → 根可取 1/2/3先纵览思路:1、2、3 三个值都可以当根。每选定一个根,左右两个子区间就定下来了,各自再递归。下面从根=1 开始一棵棵拼。
- 6左空 · 右[2,3]第一步选 1 当根。1 是最小值,左边没有更小的,所以左子树为空;右边要装下 [2,3],得先列出这个区间的所有 BST 形态。
- 72 当根,3 挂右区间 [2,3] 的第一种形态:让 2 当根。3 比 2 大,只能当 2 的右孩子,得到一条向右的小斜链 2→3。
- 8接右子树(形态1)把刚才的右子树 2→3 接到根 1 的右边(紫色是新接上的部分)。左边为空、右边这一种形态,组合出一棵完整的树。
- 91/5第 1 棵成形:1 的右边是 2、2 的右边是 3。验证一下中序遍历正好是 1、2、3 升序,是合法 BST。已生成 1 棵。
- 103 当根,2 挂左区间 [2,3] 还有第二种形态:让 3 当根。2 比 3 小,当 3 的左孩子,得到 3、左挂 2。
- 11接右子树(形态2)把第二种右子树(3、左挂 2)接到根 1 右边。这就是以 1 为根的第二棵。
- 122/5第 2 棵成形。以 1 为根的树到此有 2 棵——正是右区间 [2,3] 的两种形态各配一棵。已生成 2 棵。
- 13左[1] · 右[3]换 2 当根。左边只剩 [1]、右边只剩 [3],都是单个值,各自只有一种形态(就是它自己)。所以以 2 为根只能拼出 1 棵。
- 14左1 × 右3把单节点 1 接到 2 的左边、单节点 3 接到右边(紫色)。左右各一种,组合出唯一一棵。
- 153/5第 3 棵成形:2 当根,左右各挂一个,最平衡。中序仍是 1、2、3。已生成 3 棵。
- 16左[1,2] · 右空最后让 3 当根。3 是最大值,右边为空;左边要装 [1,2],和刚才的 [2,3] 一样,这个区间也有两种形态。
- 171 当根,2 挂右区间 [1,2] 的第一种形态:1 当根,2 比 1 大、挂它右边,得到 1→2。
- 18接左子树(形态1)把左子树(1、右挂 2)接到根 3 的左边。右边为空,组合出一棵。
- 194/5第 4 棵成形:3 当根、左边是 1、1 的右边是 2。中序还是 1、2、3。已生成 4 棵。
- 202 当根,1 挂左区间 [1,2] 的第二种形态:2 当根,1 比 2 小、挂左边,得到 2、左挂 1。
- 21接左子树(形态2)把第二种左子树(2、左挂 1)接到根 3 左边。这是以 3 为根的第二棵。
- 225/5第 5 棵成形:3、左 2、再左 1,一路向左斜。以 3 为根同样 2 棵。已生成 5 棵,全部集齐。
- 235 棵 = C₃五棵集齐。按根拆开看:以 1 为根 2 棵、以 2 为根 1 棵、以 3 为根 2 棵,2+1+2=5,正好是卡特兰数 C₃。每棵的「棵数」都等于「左区间形态数 × 右区间形态数」,这就是笛卡尔积的威力。
⚠️ 容易写错的地方
✗ 错:空区间返回空列表 []
✓ 对:空区间(lo > hi)要返回 [null]
返回 [] 会让外层 for 一次都不执行,整个根的组合全丢;[null] 表示「空也是一种形态」,笛卡尔积才完整
✗ 错:左右各取一种就拼
✓ 对:左、右所有形态两两做笛卡尔积
少了组合就会漏树,棵数会不对(应是左数 × 右数)
✗ 错:左右子区间划错
✓ 对:根 i:左 [lo, i-1]、右 [i+1, hi]
BST 性质决定比 i 小的全在左、比 i 大的全在右,边界差一个就错
完整代码(Python / C++ / Java)
Python
from typing import List, Optional
class TreeNode:
def __init__(self,val=0,left=None,right=None):
self.val=val; self.left=left; self.right=right
class Solution:
def generateTrees(self, n: int) -> List[Optional[TreeNode]]:
from functools import lru_cache
@lru_cache(None)
def build(l,r):
if l>r: return (None,)
ans=[]
for x in range(l,r+1):
for a in build(l,x-1):
for b in build(x+1,r):
ans.append(TreeNode(x,a,b))
return tuple(ans)
return list(build(1,n))C++
#include <algorithm>
#include <functional>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
using namespace std;
struct TreeNode{int val;TreeNode* left;TreeNode* right;TreeNode():val(0),left(nullptr),right(nullptr){} TreeNode(int x):val(x),left(nullptr),right(nullptr){} TreeNode(int x,TreeNode*l,TreeNode*r):val(x),left(l),right(r){}};
class Solution{public: vector<TreeNode*> generateTrees(int n){ return build(1,n);} vector<TreeNode*> build(int l,int r){ if(l>r)return {nullptr}; vector<TreeNode*> ans; for(int x=l;x<=r;x++) for(auto a:build(l,x-1)) for(auto b:build(x+1,r)) ans.push_back(new TreeNode(x,a,b)); return ans;} };Java
import java.util.*;
class TreeNode{int val;TreeNode left,right;TreeNode(){} TreeNode(int val){this.val=val;} TreeNode(int val,TreeNode left,TreeNode right){this.val=val;this.left=left;this.right=right;}}
class Solution{ public List<TreeNode> generateTrees(int n){ return build(1,n);} List<TreeNode> build(int l,int r){ ArrayList<TreeNode> ans=new ArrayList<>(); if(l>r){ ans.add(null); return ans;} for(int x=l;x<=r;x++) for(TreeNode a:build(l,x-1)) for(TreeNode b:build(x+1,r)) ans.add(new TreeNode(x,a,b)); return ans;} }复杂度
时间
O(n · Cₙ)
结果共有 Cₙ(第 n 个卡特兰数)棵树,每棵约 n 个节点要新建;Cₙ ≈ 4ⁿ / (n^1.5 · √π),增长很快
空间
O(n · Cₙ)
要把所有树都存下来返回;不计输出本身时,递归栈与中间列表为多项式级
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 不同的二叉搜索树 II 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
这题和「不同的二叉搜索树」(只求个数)是什么关系?+
那道题只要数量,直接用卡特兰数递推 dp[n] = Σ dp[i-1]·dp[n-i] 即可,O(n²);本题要真正列出每一棵结构,必须分治构造,复杂度被「棵数 = 卡特兰数」主导,高一个数量级。
能不能加记忆化加速?+
可以。相同的区间 (lo, hi) 会被不同的上层反复请求,缓存它的结果能省去重复构造;Python 解就用 lru_cache 缓存了 (lo,hi)。注意被缓存的子树会被多个父节点共享,只要后续不去修改这些子树结构就安全。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄声线把 不同的二叉搜索树 II 一步步讲透(全站 0 节语音精讲,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。