主题
前序遍历:按照「根-左子树-右子树」的顺序遍历二叉树。
中序遍历:按照「左子树-根-右子树」的顺序遍历二叉树。
我们来看看示例 1 是怎么生成这棵二叉树的。

递归边界:如果
晕递归的同学推荐先看这期视频:深入理解递归【基础算法精讲 09】
写法一
python
class Solution:
def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
if not preorder: # 空节点
return None
left_size = inorder.index(preorder[0]) # 左子树的大小
left = self.buildTree(preorder[1: 1 + left_size], inorder[:left_size])
right = self.buildTree(preorder[1 + left_size:], inorder[1 + left_size:])
return TreeNode(preorder[0], left, right)cpp
// C++ 版待补充cpp
class Solution {
// 用 span 可以避免拷贝,不用 span 的写法见【C++ 写法二】
TreeNode* build(span<int> preorder, span<int> inorder) {
if (preorder.empty()) { // 空节点
return nullptr;
}
int left_size = ranges::find(inorder, preorder[0]) - inorder.begin(); // 左子树的大小
TreeNode* left = build(preorder.subspan(1, left_size), inorder.subspan(0, left_size));
TreeNode* right = build(preorder.subspan(1 + left_size), inorder.subspan(1 + left_size));
return new TreeNode(preorder[0], left, right);
}
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
return build(preorder, inorder);
}
};cpp
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if (preorder.empty()) { // 空节点
return nullptr;
}
int left_size = ranges::find(inorder, preorder[0]) - inorder.begin(); // 左子树的大小
vector<int> pre1(preorder.begin() + 1, preorder.begin() + 1 + left_size);
vector<int> pre2(preorder.begin() + 1 + left_size, preorder.end());
vector<int> in1(inorder.begin(), inorder.begin() + left_size);
vector<int> in2(inorder.begin() + 1 + left_size, inorder.end());
TreeNode* left = buildTree(pre1, in1);
TreeNode* right = buildTree(pre2, in2);
return new TreeNode(preorder[0], left, right);
}
};复杂度分析
- 时间复杂度:
,其中 为 的长度。最坏情况下二叉树是一条链,我们需要递归 次,每次都需要 的时间查找 和复制数组。 - 空间复杂度:
。
写法二
上面的写法有两个优化点:
- 用一个哈希表(或者数组)预处理
每个元素的下标,这样就可以 查到 在 的位置,从而 知道左子树的大小。 - 把递归参数改成子数组下标区间(左闭右开区间)的左右端点,从而避免复制数组。
python
class Solution:
def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
index = {x: i for i, x in enumerate(inorder)}
# 根据 preorder[pre_l:pre_r] 和 inorder[in_l:in_r] 生成二叉树,其中 in_r 没用到,可以省略
def dfs(pre_l: int, pre_r: int, in_l: int) -> Optional[TreeNode]:
if pre_l == pre_r: # 空节点
return None
left_size = index[preorder[pre_l]] - in_l # 左子树的大小
left = dfs(pre_l + 1, pre_l + 1 + left_size, in_l)
right = dfs(pre_l + 1 + left_size, pre_r, in_l + 1 + left_size)
return TreeNode(preorder[pre_l], left, right)
return dfs(0, len(preorder), 0) # 左闭右开区间cpp
// C++ 版待补充cpp
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
int n = preorder.size();
unordered_map<int, int> index;
for (int i = 0; i < n; i++) {
index[inorder[i]] = i;
}
// 根据 preorder 的子数组 [pre_l,pre_r) 和 inorder 的子数组 [in_l,in_r) 生成二叉树,其中 in_r 没用到,可以省略
auto dfs = [&](this auto&& dfs, int pre_l, int pre_r, int in_l) -> TreeNode* {
if (pre_l == pre_r) { // 空节点
return nullptr;
}
int left_size = index[preorder[pre_l]] - in_l; // 左子树的大小
TreeNode* left = dfs(pre_l + 1, pre_l + 1 + left_size, in_l);
TreeNode* right = dfs(pre_l + 1 + left_size, pre_r, in_l + 1 + left_size);
return new TreeNode(preorder[pre_l], left, right);
};
return dfs(0, n, 0); // 左闭右开区间
}
};复杂度分析
- 时间复杂度:
,其中 为 的长度。递归 次,每次只需要 的时间。 - 空间复杂度:
。
注:由于哈希表常数比数组大,实际运行效率可能不如写法一。
构造系列
这三题都可以用本文讲的套路解决。
分类题单
- 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
- 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
- 单调栈(基础/矩形面积/贡献法/最小字典序)
- 网格图(DFS/BFS/综合应用)
- 位运算(基础/性质/拆位/试填/恒等式/思维)
- 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
- 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
- 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
- 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
- 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
- 链表、树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA)
- 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)
欢迎关注 B站@灵茶山艾府