题目LeetCode105
给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历 , inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。
示例

输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]
Python解法
解法一(递归DFS)
python
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
if not preorder or not inorder:
return None
# 快速找根在中序的位置
val2idx = {val: i for i, val in enumerate(inorder)}
def dfs(pre_l: int, pre_r: int, in_l: int, in_r: int) -> Optional[TreeNode]:
if pre_l > pre_r:
return None
# 根节点
root_val = preorder[pre_l]
root = TreeNode(root_val)
# 左子树大小
pos = val2idx[root_val]
left_size = pos - in_l
# 递归左右
root.left = dfs(pre_l + 1, pre_l + left_size, in_l, pos - 1)
root.right = dfs(pre_l + left_size + 1, pre_r, pos + 1, in_r)
return root
return dfs(0, len(preorder) - 1, 0, len(inorder) - 1)
解释
这里解释enumerate哈希表映射
① enumerate(inorder)
给 inorder 数组同时返回 下标 i + 元素 val 比如 inorder = [9,3,15,20,7]
遍历会得到:
python
i=0, val=9
i=1, val=3
i=2, val=15
i=3, val=20
i=4, val=7
② for i, val in enumerate(...)
循环取出每一组 (下标 i, 值 val)
③ {val: i ...}
构建字典:key = 节点值,value = 下标
最终得到:
python
{
9: 0,
3: 1,
15: 2,
20: 3,
7: 4
}
过程演示

解法二(迭代BFS)
python
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
if not preorder or not inorder:
return None
root = TreeNode(preorder[0])
stack = [root]
in_idx = 0 # 语义更清晰
for i in range(1, len(preorder)):
cur_val = preorder[i]
node = stack[-1]
# 一路向左走
if node.val != inorder[in_idx]:
node.left = TreeNode(cur_val)
stack.append(node.left)
# 回溯,找第一个能挂右孩子的节点
else:
while stack and stack[-1].val == inorder[in_idx]:
node = stack.pop()
in_idx += 1
node.right = TreeNode(cur_val)
stack.append(node.right)
return root
解释
这道迭代法的本质是:
- 先一路沿着
preorder扎到最左,全部入栈 - 当栈顶 == 中序当前指向,说明左子树走完了
- 不断出栈回溯 ,找到第一个能挂右孩子的节点
- 把当前
preorder值挂成它的右孩子,继续
过程演示

Java解法
解法一(递归DFS)
java
import java.util.HashMap;
import java.util.Map;
// 二叉树节点定义
class TreeNode {
int val;
TreeNode left;
TreeNode 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 {
// 哈希表:存放中序数组 值 -> 下标,快速查找根位置
private Map<Integer, Integer> valueToIndex;
public TreeNode buildTree(int[] preorder, int[] inorder) {
valueToIndex = new HashMap<>();
int len = inorder.length;
// 预处理,把中序所有元素下标存入哈希
for (int i = 0; i < len; i++) {
valueToIndex.put(inorder[i], i);
}
// 递归构建:前序左右边界,中序左右边界
return dfs(preorder, 0, len - 1, 0, len - 1);
}
/**
* 深度优先递归建树
* @param pre 前序数组
* @param preL 前序左边界
* @param preR 前序右边界
* @param inL 中序左边界
* @param inR 中序右边界
* @return 构建好的子树根节点
*/
private TreeNode dfs(int[] pre, int preL, int preR, int inL, int inR) {
// 区间无效,没有节点,返回空
if (preL > preR) {
return null;
}
// 前序第一个位置就是当前根节点
int rootVal = pre[preL];
TreeNode root = new TreeNode(rootVal);
// 获取根节点在中序数组中的下标
int rootPos = valueToIndex.get(rootVal);
// 计算左子树节点个数:中序根位置 - 中序左边界
int leftSubSize = rootPos - inL;
// 递归构建左子树
root.left = dfs(pre, preL + 1, preL + leftSubSize, inL, rootPos - 1);
// 递归构建右子树
root.right = dfs(pre, preL + leftSubSize + 1, preR, rootPos + 1, inR);
return root;
}
}
解法二(迭代BFS)
java
import java.util.Stack;
class TreeNode {
int val;
TreeNode left;
TreeNode 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 TreeNode buildTree(int[] preorder, int[] inorder) {
// 空数组直接返回空树
if (preorder.length == 0) return null;
// 前序第一个元素为整棵树根节点
TreeNode root = new TreeNode(preorder[0]);
// 栈用来模拟递归深度,保存遍历路径上的节点
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
// 中序遍历指针,记录当前走到中序哪个位置
int inIndex = 0;
// 遍历前序剩余所有节点
for (int i = 1; i < preorder.length; i++) {
int curVal = preorder[i];
// 获取当前栈顶节点
TreeNode topNode = stack.peek();
// 栈顶值 != 中序当前值:继续往左子树走
if (topNode.val != inorder[inIndex]) {
topNode.left = new TreeNode(curVal);
stack.push(topNode.left);
}
// 相等说明左子树走完,开始回溯找右子树挂载点
else {
// 不断出栈,同步移动中序指针
while (!stack.isEmpty() && stack.peek().val == inorder[inIndex]) {
topNode = stack.pop();
inIndex++;
}
// 把当前节点作为回溯后节点的右孩子
topNode.right = new TreeNode(curVal);
stack.push(topNode.right);
}
}
return root;
}
}
C++解法
解法一(递归DFS)
cpp
#include <vector>
#include <unordered_map>
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 *left, TreeNode *right) : val(x), left(left), right(right) {}
};
class Solution {
private:
// 哈希映射:中序值对应下标
unordered_map<int, int> val_idx_map;
/**
* 递归深度优先构建二叉树
*/
TreeNode* dfs(vector<int>& pre, int preL, int preR, int inL, int inR) {
// 区间越界,无节点
if (preL > preR) return nullptr;
// 取当前区间前序首位为根
int root_val = pre[preL];
TreeNode* root = new TreeNode(root_val);
// 查到根在中序中的位置
int root_pos = val_idx_map[root_val];
// 统计左子树节点数量
int left_size = root_pos - inL;
// 构建左子树
root->left = dfs(pre, preL + 1, preL + left_size, inL, root_pos - 1);
// 构建右子树
root->right = dfs(pre, preL + left_size + 1, preR, root_pos + 1, inR);
return root;
}
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
int n = inorder.size();
// 预处理建立值与下标的映射
for (int i = 0; i < n; ++i) {
val_idx_map[inorder[i]] = i;
}
// 初始左右边界都是整个数组
return dfs(preorder, 0, n - 1, 0, n - 1);
}
};
解法二(迭代BFS)
cpp
#include <vector>
#include <stack>
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 *left, TreeNode *right) : val(x), left(left), right(nullptr) {}
};
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
// 判空
if (preorder.empty()) return nullptr;
// 初始化根节点
TreeNode* root = new TreeNode(preorder[0]);
// 栈存储遍历路径节点
stack<TreeNode*> stk;
stk.push(root);
// 中序遍历索引指针
int in_idx = 0;
// 遍历前序数组后续元素
for (int i = 1; i < preorder.size(); ++i) {
int cur_val = preorder[i];
TreeNode* top = stk.top();
// 不相等,挂载左子树
if (top->val != inorder[in_idx]) {
top->left = new TreeNode(cur_val);
stk.push(top->left);
}
// 相等,回溯出栈,找右子树父节点
else {
while (!stk.empty() && stk.top()->val == inorder[in_idx]) {
top = stk.top();
stk.pop();
in_idx++;
}
// 挂载右孩子
top->right = new TreeNode(cur_val);
stk.push(top->right);
}
}
return root;
}
};
三种语言栈操作对比可参考之前文章末尾,如下链接https://blog.csdn.net/2602_95354978/article/details/161232212?spm=1011.2415.3001.5331