二叉树十题通关:从层序遍历到序列化(Python + C++)
二叉树是面试中的重中之重,涉及递归、迭代、BFS、DFS、分治等多种思想。本文整理了10道经典题目,每道题包含:题目描述、解题思路、图解(文本示意)、Python代码、C++代码、复杂度分析。掌握这些,二叉树类题目基本通关。
📌 题目清单
| 题号 | 题目 | 核心考点 |
|---|---|---|
| 102 | 二叉树的层序遍历 | BFS(队列) |
| 104 | 二叉树的最大深度 | DFS / BFS |
| 226 | 翻转二叉树 | 递归 / 迭代 |
| 94 | 二叉树的中序遍历 | 迭代(栈)必会 |
| 105 | 从前序与中序遍历序列构造二叉树 | 递归分治 |
| 98 | 验证二叉搜索树 | 中序遍历有序 / 递归传递范围 |
| 236 | 二叉树的最近公共祖先 | 递归(后序遍历) |
| 114 | 二叉树展开为链表 | 递归 / 迭代(右子树链接) |
| 199 | 二叉树的右视图 | BFS层序(每层最后一个) |
| 297 | 二叉树的序列化与反序列化 | BFS / DFS 设计(高频) |
注:以下题目中使用的二叉树节点定义统一为:
pythonclass TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = rightC++ 节点定义:
cppstruct 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) {} };
1. 二叉树的层序遍历(LeetCode 102)
题目描述
给你二叉树的根节点 root,返回其节点值的层序遍历(即逐层地,从左到右访问所有节点)。
示例 :
输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]
解题思路
- 使用队列进行广度优先搜索(BFS)。
- 每次处理一层的所有节点,将它们的值加入当前层列表,并将其子节点加入队列。
- 循环直到队列空。
图解
3
/ \
9 20
/ \
15 7
队列初始: [3]
处理第0层: 取出3 → 加入子节点9,20 → 当前层[3]
处理第1层: 取出9,20 → 9无子节点,20加入15,7 → 当前层[9,20]
处理第2层: 取出15,7 → 当前层[15,7]
结果 [[3],[9,20],[15,7]]
Python代码
python
from collections import deque
def levelOrder(root):
if not root:
return []
res = []
q = deque([root])
while q:
level = []
for _ in range(len(q)):
node = q.popleft()
level.append(node.val)
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
res.append(level)
return res
C++代码
cpp
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
if (!root) return {};
vector<vector<int>> res;
queue<TreeNode*> q;
q.push(root);
while (!q.empty()) {
int size = q.size();
vector<int> level;
for (int i = 0; i < size; ++i) {
TreeNode* node = q.front(); q.pop();
level.push_back(node->val);
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
res.push_back(level);
}
return res;
}
};
复杂度分析
- 时间复杂度:O(n),每个节点访问一次。
- 空间复杂度:O(n),队列最坏存储满二叉树最后一层节点数约 n/2。
2. 二叉树的最大深度(LeetCode 104)
题目描述
给定一个二叉树,找出其最大深度。二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
示例 :
输入:root = [3,9,20,null,null,15,7] → 输出:3
解题思路
- 递归(DFS):最大深度 = 1 + max(左子树深度, 右子树深度)。
- 迭代(BFS):层序遍历的层数即为最大深度。
图解(递归)
3
/ \
9 20
/ \
15 7
depth(3)=1+max(depth(9),depth(20))
depth(9)=1+max(0,0)=1
depth(20)=1+max(depth(15),depth(7))=1+1=2
depth(3)=1+max(1,2)=3
Python代码(递归)
python
def maxDepth(root):
if not root:
return 0
return 1 + max(maxDepth(root.left), maxDepth(root.right))
C++代码(递归)
cpp
class Solution {
public:
int maxDepth(TreeNode* root) {
if (!root) return 0;
return 1 + max(maxDepth(root->left), maxDepth(root->right));
}
};
Python代码(BFS)
python
def maxDepth(root):
if not root:
return 0
q = deque([root])
depth = 0
while q:
depth += 1
for _ in range(len(q)):
node = q.popleft()
if node.left: q.append(node.left)
if node.right: q.append(node.right)
return depth
复杂度分析
- 时间复杂度:O(n)。
- 空间复杂度:递归 O(height)(最差 O(n)),BFS O(n)。
3. 翻转二叉树(LeetCode 226)
题目描述
翻转一棵二叉树(左右子树交换)。
示例 :
输入:[4,2,7,1,3,6,9] → 输出:[4,7,2,9,6,3,1]
解题思路
- 递归:交换当前节点的左右子树,然后递归翻转左右子树。
- 迭代:使用队列或栈,每次交换节点的左右孩子。
图解
4 4
/ \ / \
2 7 → 7 2
/ \ / \ / \ / \
1 3 6 9 9 6 3 1
Python代码(递归)
python
def invertTree(root):
if not root:
return None
root.left, root.right = root.right, root.left
invertTree(root.left)
invertTree(root.right)
return root
C++代码(递归)
cpp
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if (!root) return nullptr;
swap(root->left, root->right);
invertTree(root->left);
invertTree(root->right);
return root;
}
};
Python代码(迭代)
python
def invertTree(root):
if not root:
return None
q = deque([root])
while q:
node = q.popleft()
node.left, node.right = node.right, node.left
if node.left: q.append(node.left)
if node.right: q.append(node.right)
return root
复杂度分析
- 时间复杂度:O(n)。
- 空间复杂度:递归 O(height),迭代 O(n)。
4. 二叉树的中序遍历(LeetCode 94)
题目描述
给定一个二叉树的根节点,返回它的中序遍历(左-根-右)。
示例 :
输入:[1,null,2,3] → 输出:[1,3,2]
解题思路
- 递归:左子树 → 根 → 右子树。
- 迭代(必会):使用栈模拟递归过程。一直向左压栈,然后弹出访问,再转向右子树。
图解(迭代)
1
\
2
/
3
栈: []
cur=1 → 压1, cur=左(空)
弹出1访问 → cur=2
压2, cur=左(3)
压3, cur=左(空)
弹出3访问 → cur=右(空)
弹出2访问
结果 [1,3,2]
Python代码(递归)
python
def inorderTraversal(root):
res = []
def dfs(node):
if not node:
return
dfs(node.left)
res.append(node.val)
dfs(node.right)
dfs(root)
return res
C++代码(递归)
cpp
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
inorder(root, res);
return res;
}
void inorder(TreeNode* node, vector<int>& res) {
if (!node) return;
inorder(node->left, res);
res.push_back(node->val);
inorder(node->right, res);
}
};
Python代码(迭代,必会)
python
def inorderTraversal(root):
res = []
stack = []
cur = root
while cur or stack:
while cur:
stack.append(cur)
cur = cur.left
cur = stack.pop()
res.append(cur.val)
cur = cur.right
return res
C++代码(迭代)
cpp
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> st;
TreeNode* cur = root;
while (cur || !st.empty()) {
while (cur) {
st.push(cur);
cur = cur->left;
}
cur = st.top(); st.pop();
res.push_back(cur->val);
cur = cur->right;
}
return res;
}
};
复杂度分析
- 时间复杂度:O(n)。
- 空间复杂度:递归 O(height),迭代 O(height)。
5. 从前序与中序遍历序列构造二叉树(LeetCode 105)
题目描述
给定一棵二叉树的前序遍历 preorder 和中序遍历 inorder,构造该二叉树。假设树中没有重复元素。
示例 :
输入:preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出:[3,9,20,null,null,15,7]
解题思路
- 前序遍历的第一个元素是根节点。
- 在中序遍历中找到根节点的位置,左边为左子树的中序,右边为右子树的中序。
- 根据左右子树的大小,在前序遍历中切分出左子树的前序和右子树的前序。
- 递归构建。
图解
pre: [3,9,20,15,7]
in: [9,3,15,20,7]
根 = 3
左子树中序 [9] → 左子树大小1 → 左子树前序 [9]
右子树中序 [15,20,7] → 右子树前序 [20,15,7]
递归构建左右子树
Python代码
python
def buildTree(preorder, inorder):
if not preorder:
return None
root_val = preorder[0]
root = TreeNode(root_val)
idx = inorder.index(root_val)
left_inorder = inorder[:idx]
right_inorder = inorder[idx+1:]
left_preorder = preorder[1:1+len(left_inorder)]
right_preorder = preorder[1+len(left_inorder):]
root.left = buildTree(left_preorder, left_inorder)
root.right = buildTree(right_preorder, right_inorder)
return root
C++代码(使用哈希表优化查找)
cpp
class Solution {
public:
unordered_map<int, int> idxMap;
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
for (int i = 0; i < inorder.size(); ++i) idxMap[inorder[i]] = i;
return build(preorder, 0, preorder.size()-1, inorder, 0, inorder.size()-1);
}
TreeNode* build(vector<int>& pre, int pl, int pr, vector<int>& in, int il, int ir) {
if (pl > pr) return nullptr;
int rootVal = pre[pl];
TreeNode* root = new TreeNode(rootVal);
int idx = idxMap[rootVal];
int leftSize = idx - il;
root->left = build(pre, pl+1, pl+leftSize, in, il, idx-1);
root->right = build(pre, pl+leftSize+1, pr, in, idx+1, ir);
return root;
}
};
复杂度分析
- 时间复杂度:O(n),哈希表优化后每个节点处理一次。
- 空间复杂度:O(n),递归栈和哈希表。
6. 验证二叉搜索树(LeetCode 98)
题目描述
给定一个二叉树,判断其是否是一个有效的二叉搜索树(BST)。BST 定义:左子树所有节点值 < 根节点值 < 右子树所有节点值。
示例 :
输入:[2,1,3] → 输出:true
输入:[5,1,4,null,null,3,6] → 输出:false
解题思路
- 方法一(中序遍历):BST 的中序遍历结果是严格递增的。遍历过程中检查前驱节点值是否小于当前节点。
- 方法二(递归传递范围):每个节点有一个合法的取值范围 (min, max),递归检查。
图解(中序遍历)
2
/ \
1 3
中序: [1,2,3] 递增 → true
Python代码(中序遍历)
python
def isValidBST(root):
stack = []
cur = root
prev = None
while cur or stack:
while cur:
stack.append(cur)
cur = cur.left
cur = stack.pop()
if prev is not None and prev.val >= cur.val:
return False
prev = cur
cur = cur.right
return True
C++代码(递归范围)
cpp
class Solution {
public:
bool isValidBST(TreeNode* root) {
return dfs(root, LONG_MIN, LONG_MAX);
}
bool dfs(TreeNode* node, long lower, long upper) {
if (!node) return true;
if (node->val <= lower || node->val >= upper) return false;
return dfs(node->left, lower, node->val) && dfs(node->right, node->val, upper);
}
};
复杂度分析
- 时间复杂度:O(n)。
- 空间复杂度:O(height)。
7. 二叉树的最近公共祖先(LeetCode 236)
题目描述
给定一棵二叉树和两个节点 p、q,找出它们的最近公共祖先(LCA)。
示例 :
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p=5, q=1 → 输出:3
解题思路
- 递归后序遍历:
- 如果当前节点为 null,返回 null。
- 如果当前节点等于 p 或 q,返回当前节点。
- 递归在左右子树中寻找 p 和 q。
- 如果左右子树返回值都不为空,说明当前节点就是 LCA。
- 如果一边为空,返回另一边。
图解
3
/ \
5 1
/ \ / \
6 2 0 8
/ \
7 4
p=5, q=4
递归到5的左子树6 -> null, 右子树2 -> 2的左7 null 右4返回4 -> 2的左右返回(null,4)-> 返回4
5的左右返回(null,4)-> 返回4? 不对,应返回5(因为5等于p)。需修正逻辑:若节点等于p或q直接返回自身。
正确过程:5等于p,直接返回5,不再深入。同时右子树递归返回4,但5已经返回了,所以祖先为5。
Python代码
python
def lowestCommonAncestor(root, p, q):
if not root or root == p or root == q:
return root
left = lowestCommonAncestor(root.left, p, q)
right = lowestCommonAncestor(root.right, p, q)
if left and right:
return root
return left or right
C++代码
cpp
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (!root || root == p || root == q) return root;
TreeNode* left = lowestCommonAncestor(root->left, p, q);
TreeNode* right = lowestCommonAncestor(root->right, p, q);
if (left && right) return root;
return left ? left : right;
}
};
复杂度分析
- 时间复杂度:O(n)。
- 空间复杂度:O(height)。
8. 二叉树展开为链表(LeetCode 114)
题目描述
给定一个二叉树,将其原地展开为单链表(使用右孩子指针指向下一个节点,左孩子始终为 null)。
示例 :
输入:[1,2,5,3,4,null,6] → 输出:[1,null,2,null,3,null,4,null,5,null,6]
解题思路
- 递归:先展平左子树,再展平右子树,然后将左子树接到根与右子树之间。
- 迭代:从根开始,如果左子树存在,找到左子树的最右节点,将原右子树接到该最右节点的右边,然后将左子树整体移到右边。
图解(迭代)
1
/ \
2 5
/ \ \
3 4 6
步骤1: 1的左子树2存在,找左子树最右节点4,将5接到4.right,然后将2作为1.right,左孩子置空
1
\
2
/ \
3 4
\
5
\
6
然后处理节点2,重复...
最终得到右链表
Python代码(递归)
python
def flatten(root):
if not root:
return
flatten(root.left)
flatten(root.right)
left = root.left
right = root.right
root.left = None
root.right = left
# 找到当前右子树的末尾
p = root
while p.right:
p = p.right
p.right = right
C++代码(迭代)
cpp
class Solution {
public:
void flatten(TreeNode* root) {
TreeNode* cur = root;
while (cur) {
if (cur->left) {
TreeNode* leftRight = cur->left;
while (leftRight->right) leftRight = leftRight->right;
leftRight->right = cur->right;
cur->right = cur->left;
cur->left = nullptr;
}
cur = cur->right;
}
}
};
复杂度分析
- 时间复杂度:O(n),每个节点访问常数次。
- 空间复杂度:递归 O(height),迭代 O(1)。
9. 二叉树的右视图(LeetCode 199)
题目描述
给定一棵二叉树,想象自己站在它的右侧,返回从顶部到底部看到的节点值。
示例 :
输入:[1,2,3,null,5,null,4] → 输出:[1,3,4]
解题思路
- 使用层序遍历(BFS),每层取最后一个节点的值。
- 也可以使用 DFS 先右后左,记录深度,第一次访问该深度的节点即为右视图。
图解(BFS)
1
/ \
2 3
\ \
5 4
层序: 第0层[1] → 取1
第1层[2,3] → 取3
第2层[5,4] → 取4
结果 [1,3,4]
Python代码(BFS)
python
def rightSideView(root):
if not root:
return []
res = []
q = deque([root])
while q:
level_size = len(q)
for i in range(level_size):
node = q.popleft()
if i == level_size - 1:
res.append(node.val)
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
return res
C++代码(DFS,先右后左)
cpp
class Solution {
public:
vector<int> rightSideView(TreeNode* root) {
vector<int> res;
dfs(root, 0, res);
return res;
}
void dfs(TreeNode* node, int depth, vector<int>& res) {
if (!node) return;
if (depth == res.size()) res.push_back(node->val);
dfs(node->right, depth+1, res);
dfs(node->left, depth+1, res);
}
};
复杂度分析
- 时间复杂度:O(n)。
- 空间复杂度:BFS O(n),DFS O(height)。
10. 二叉树的序列化与反序列化(LeetCode 297)
题目描述
设计一个算法,将二叉树序列化为字符串,并能从字符串反序列化回原树。不限制格式,只需能还原。
示例 :
输入:[1,2,3,null,null,4,5] → 序列化 → 反序列化还原。
解题思路
- BFS(层序) :使用队列进行层序遍历,将空节点标记为
"null",用分隔符连接。反序列化时同样用队列按层构建。 - DFS(前序):递归前序遍历,遇到 null 也记录,反序列化时同样递归构建。
图解(BFS)
1
/ \
2 3
/ \
4 5
序列化: "1,2,3,null,null,4,5,null,null,null,null"
反序列化: 用队列存储节点,依次赋值左右孩子
Python代码(BFS 序列化)
python
from collections import deque
class Codec:
def serialize(self, root):
if not root:
return ""
q = deque([root])
res = []
while q:
node = q.popleft()
if node:
res.append(str(node.val))
q.append(node.left)
q.append(node.right)
else:
res.append("null")
return ",".join(res)
def deserialize(self, data):
if not data:
return None
vals = data.split(",")
root = TreeNode(int(vals[0]))
q = deque([root])
i = 1
while q:
node = q.popleft()
if vals[i] != "null":
node.left = TreeNode(int(vals[i]))
q.append(node.left)
i += 1
if vals[i] != "null":
node.right = TreeNode(int(vals[i]))
q.append(node.right)
i += 1
return root
C++代码(DFS 前序)
cpp
class Codec {
public:
string serialize(TreeNode* root) {
ostringstream out;
serialize(root, out);
return out.str();
}
void serialize(TreeNode* root, ostringstream& out) {
if (!root) out << "null ";
else {
out << root->val << " ";
serialize(root->left, out);
serialize(root->right, out);
}
}
TreeNode* deserialize(string data) {
istringstream in(data);
return deserialize(in);
}
TreeNode* deserialize(istringstream& in) {
string val;
in >> val;
if (val == "null") return nullptr;
TreeNode* node = new TreeNode(stoi(val));
node->left = deserialize(in);
node->right = deserialize(in);
return node;
}
};
复杂度分析
- 时间复杂度:O(n),每个节点访问一次。
- 空间复杂度:O(n),存储序列化字符串和队列/栈。
🎯 总结
| 题目 | 核心技巧 | 时间复杂度 | 空间复杂度 |
|---|---|---|---|
| 102. 层序遍历 | BFS(队列) | O(n) | O(n) |
| 104. 最大深度 | DFS / BFS | O(n) | O(height) / O(n) |
| 226. 翻转二叉树 | 递归交换子树 | O(n) | O(height) |
| 94. 中序遍历 | 迭代栈(必会) | O(n) | O(height) |
| 105. 构建二叉树 | 前中序分治 + 哈希表 | O(n) | O(n) |
| 98. 验证BST | 中序递增 / 范围递归 | O(n) | O(height) |
| 236. 最近公共祖先 | 后序递归 | O(n) | O(height) |
| 114. 展开链表 | 迭代(右子树链接) | O(n) | O(1) |
| 199. 右视图 | BFS每层最后一个 | O(n) | O(n) |
| 297. 序列化 | BFS/DFS + 标记空节点 | O(n) | O(n) |
二叉树的题目主要考察递归思维、遍历框架(前中后序、层序)以及如何利用遍历顺序解决问题。建议熟练掌握递归和迭代两种写法,尤其是中序遍历的迭代实现(面试高频)。