二叉树
一、二叉树基础概念与分类
- 基本定义
二叉树是每个节点最多有两个子节点的树结构,包含根节点、左子树、右子树。核心特性包括高度、深度、层级等。 - 常见分类
- 满二叉树:所有非叶子节点均有2个子节点。
- 完全二叉树:除最后一层外均为满结构,最后一层节点左对齐。
- 二叉搜索树(BST):左子树所有节点值小于根节点,右子树所有节点值大于根节点。
- 平衡二叉树(AVL树):任意节点左右子树高度差不超过1,优化查询效率。
二、核心操作与遍历算法
-
遍历方法
遍历类型 递归实现 非递归实现(栈/队列) 应用场景 前序遍历 根→左→右 栈模拟 复制二叉树结构 中序遍历 左→根→右 栈模拟 二叉搜索树升序输出 后序遍历 左→右→根 栈模拟+反转 计算子树节点数量 层序遍历 队列分层处理 队列迭代 求树宽度、锯齿形遍历 示例代码(C++层序遍历递归解法):
void levelOrder(TreeNode* root, int level, vector<vector<int>>& res) { if (!root) return; if (res.size() == level) res.push_back({}); res[level].push_back(root->val); levelOrder(root->left, level + 1, res); levelOrder(root->right, level + 1, res); }
-
高频操作
- 翻转二叉树:递归交换左右子树(LeetCode 226)。
- 求最大深度:递归取左右子树深度最大值 +1(LeetCode 104)。
- 求最小深度:需处理单子树为空的情况(LeetCode 111)。
三、高频面试题型与解析
-
基础性质判断
- 相同树判断:递归比较根节点值及左右子树是否相同(LeetCode 100)。
- 对称树判断:递归比较左右子树镜像,或队列迭代判断(LeetCode 101)。
-
结构操作类
- 合并二叉树:递归叠加节点值,处理空子树情况(LeetCode 617)。
- 二叉树展开为链表:前序遍历+修改指针(LeetCode 114)。
-
路径与序列问题
- 路径总和:递归回溯判断路径和是否等于目标值(LeetCode 112)。
- 二叉树的序列化:前序遍历生成字符串,反序列化时递归解析(LeetCode 297)。
-
高级应用
- 最近公共祖先(LCA):后序遍历递归查找目标节点(LeetCode 236)。
- 构建二叉树:根据前序+中序或中序+后序序列重建(LeetCode 105/106)。
四、二叉树变种与工程应用
-
二叉搜索树(BST)
- 验证BST:中序遍历检查是否递增(LeetCode 98)。
- BST插入/删除:保持有序性,删除时处理子节点替换。
-
平衡二叉树
- AVL树旋转:通过左旋、右旋调整平衡因子。
- 红黑树特性:近似平衡,广泛用于STL(如C++的
map
)。
-
堆结构
- 完全二叉树实现:大根堆/小根堆用于优先队列、堆排序(LeetCode 215)。
五、学习建议与高频考点总结
-
刷题重点
题型 核心技巧 相关题目 遍历算法 递归与非递归实现 LeetCode 144/94/145/102 子树判断 递归匹配结构 LeetCode 572 路径问题 回溯+剪枝 LeetCode 113/124 二叉树构建 分治法处理序列 LeetCode 105/106 -
注意事项
- 边界条件:处理空树、单节点、不平衡树等场景。
- 复杂度优化:递归可能导致栈溢出,需掌握迭代解法。
- 工程应用:红黑树在数据库索引、语言标准库中的使用。
高频题
- 二叉树遍历(前序、中序、后序):递归法,前序(根-左-右),中序(左-根-右),后序(左-右-根)。
//前序
void preorder(TreeNode* root, vector<int>& res) {
if (!root) return;
res.push_back(root->val); // 访问根节点
preorder(root->left, res); // 递归左子树
preorder(root->right, res); // 递归右子树
}
//中序
void inorder(TreeNode* root, vector<int>& res) {
if (!root) return;
inorder(root->left, res); // 递归左子树
res.push_back(root->val); // 访问根节点
inorder(root->right, res); // 递归右子树
}
//后续
void postorder(TreeNode* root, vector<int>& res) {
if (!root) return;
postorder(root->left, res); // 递归左子树
postorder(root->right, res); // 递归右子树
res.push_back(root->val); // 访问根节点
}
- 层序遍历:即逐层的从左到后访问所有节点,用广度优先遍历 BFS,使用队列保存当前层节点,循环处理每个节点并记录值,同时将子节点入队。
#include <queue>
#include <vector>
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
vector<int> levelOrder(TreeNode* root) {
vector<int> res;
if (!root) return res;
queue<TreeNode*> q;//创建队列
q.push(root); //根节点压入队列
//当队列不为空时,循环处理节点
while (!q.empty()) {
TreeNode* node = q.front(); //获取队列的前端节点,即当前层的节点
q.pop(); //移除队列的前端的节点
res.push_back(node->val); //节点值加到结果数组里
if (node->left) q.push(node->left); //左子节点入队
if (node->right) q.push(node->right); //右子节点入队
}
return res;
}
- 二叉树的最大深度:递归法,树的最大深度等于左右子树的最大深度加 1。
int maxDepth(TreeNode* root) {
if (!root) return 0; // 空树深度为0
int left = maxDepth(root->left);
int right = maxDepth(root->right);
return max(left, right) + 1; // 取左右子树深度的较大者加1
}
- 二叉树最小深度:利用队列进行广度优先搜索,找到第一个叶子节点时候返回当前层级。(叶子节点是指没有子节点,即没有左子节点和右子节点。)
int minDepth(TreeNode* root) {
if (!root) return 0;
queue<TreeNode*> q;
q.push(root);
int depth = 0;
while (!q.empty()) {
depth++;
int size = q.size();
for (int i = 0; i < size; i++) {
TreeNode* node = q.front();
q.pop();
// 发现叶子节点立即返回
if (!node->left && !node->right) return depth;
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
}
return depth; // 保证语法完整性
}
- 相同的树:递归法,即两个二叉树当前节点值相等,左右子树结构相同即为相同的树。
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
bool isSameTree(TreeNode* p, TreeNode* q) {
// 两个节点均为空,视为相同
if (!p && !q) return true;
// 一个为空另一个非空,结构不同
if (!p || !q) return false;
// 节点值不同则直接返回false
if (p->val != q->val) return false;
// 递归检查左右子树
return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}
- 对称二叉树:递归法,即左子树的左节点与右子树的右节点、左子树的右节点与右子树的左节点需同时满足对称条件。
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
bool isSymmetric(TreeNode* root) {
if (!root) return true;
return compare(root->left, root->right);
}
bool compare(TreeNode* left, TreeNode* right) {
// 基例处理
if (!left && !right) return true; // 均为空节点
if (!left || !right) return false; // 仅一个为空
if (left->val != right->val) return false; // 节点值不同
// 递归比较内外侧子树
bool outside = compare(left->left, right->right); // 外侧对称(左左 vs 右右)
bool inside = compare(left->right, right->left); // 内侧对称(左右 vs 右左)
return outside && inside;
}
- 翻转二叉树:通过递归交换每个节点的左右子树,实现自顶向下的翻转。
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
TreeNode* invertTree(TreeNode* root) {
if (!root) return nullptr; // 递归终止条件
// 交换当前节点的左右子树
TreeNode* temp = root->left;
root->left = root->right;
root->right = temp;
// 递归处理子节点
invertTree(root->left);
invertTree(root->right);
return root;
}