深入掌握二叉树的核心概念!今日带你全面解析二叉树的存储结构、遍历算法与构建技巧,结合大厂高频真题案例,迅速提升数据结构实战能力。
一、二叉树核心概念
二叉树(Binary Tree)是一种每个节点最多有两个子节点的数据结构,其基本特点为:
- 节点结构: 每个节点包含数据域以及指向左子树和右子树的指针
- 存储方式: 链式存储(指针实现)或顺序存储(数组模拟完全二叉树)
- 遍历方式: 前序、中序、后序(深度优先遍历) & 层序(广度优先遍历)
基本节点的C++定义:
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
二、算法原理与步骤分解
1. 二叉树遍历方法
二叉树的遍历是解决众多题目(例如路径问题、搜索问题)的基础。常见遍历方法包括:
- 前序遍历: 根节点 → 左子树 → 右子树
- 中序遍历: 左子树 → 根节点 → 右子树
- 后序遍历: 左子树 → 右子树 → 根节点
- 层序遍历: 逐层从上到下、从左到右依次访问
2. 遍历的递归实现(示例:中序遍历)
使用递归可快速实现深度优先遍历:
#include <iostream>
#include <vector>
using namespace std;
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
void inorderTraversal(TreeNode* root, vector<int>& res) {
if (root == nullptr) return;
inorderTraversal(root->left, res); // 访问左子树
res.push_back(root->val); // 访问当前节点
inorderTraversal(root->right, res); // 访问右子树
}
// 测试用例
int main() {
/*
1
/ \
2 3
/ \
4 5
*/
TreeNode *root = new TreeNode(1);
root->left = new TreeNode(2);
root->right = new TreeNode(3);
root->left->left = new TreeNode(4);
root->left->right = new TreeNode(5);
vector<int> result;
inorderTraversal(root, result);
cout << "中序遍历结果:";
for (int num : result)
cout << num << " ";
return 0;
}
3. 层序遍历(广度优先搜索)
层序遍历通常借助队列,适用于搜索最短路径、判断二叉树完全性等场景:
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
void levelOrderTraversal(TreeNode* root, vector<vector<int>>& res) {
if (!root) return;
queue<TreeNode*> q;
q.push(root);
while (!q.empty()) {
int levelSize = q.size();
vector<int> level;
while (levelSize--) {
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);
}
}
// 测试层序遍历
int main() {
/*
1
/ \
2 3
/ \
4 5
*/
TreeNode *root = new TreeNode(1);
root->left = new TreeNode(2);
root->right = new TreeNode(3);
root->left->left = new TreeNode(4);
root->left->right = new TreeNode(5);
vector<vector<int>> levels;
levelOrderTraversal(root, levels);
cout << "层序遍历结果:" << endl;
for (auto level : levels) {
for (int num : level)
cout << num << " ";
cout << endl;
}
return 0;
}
三、完整二叉树算法实现(C++)
1. 构建二叉树:通过前序和中序遍历重构二叉树
不少厂商真题涉及从遍历序列构造树的问题。下面给出具体实现:
#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
TreeNode* buildTreeHelper(vector<int>& preorder, int preStart, int preEnd,
vector<int>& inorder, int inStart, int inEnd,
unordered_map<int, int>& inMap) {
if (preStart > preEnd || inStart > inEnd)
return nullptr;
int rootVal = preorder[preStart];
TreeNode* root = new TreeNode(rootVal);
int inRoot = inMap[rootVal];
int numsLeft = inRoot - inStart;
root->left = buildTreeHelper(preorder, preStart + 1, preStart + numsLeft,
inorder, inStart, inRoot - 1, inMap);
root->right = buildTreeHelper(preorder, preStart + numsLeft + 1, preEnd,
inorder, inRoot + 1, inEnd, inMap);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
unordered_map<int, int> inMap;
for (int i = 0; i < inorder.size(); i++)
inMap[inorder[i]] = i;
return buildTreeHelper(preorder, 0, preorder.size()-1, inorder, 0, inorder.size()-1, inMap);
}
// 测试用例
int main(){
vector<int> preorder = {3,9,20,15,7};
vector<int> inorder = {9,3,15,20,7};
TreeNode* root = buildTree(preorder, inorder);
// 此处可以调用前序、中序或层序遍历检测构建结果
cout << "二叉树重构成功!" << endl;
return 0;
}
2. 执行过程示例
以前序遍历序列 [3, 9, 20, 15, 7]
与中序遍历序列 [9, 3, 15, 20, 7]
为例:
- 构树步骤:
- 根节点确定为 3
- 在中序序列中查找 3,将序列分割为左子树
[9]
和右子树[15,20,7]
- 递归构建左右子树,直至恢复完整的树结构
四、复杂度与优化
步骤 | 时间复杂度 | 空间复杂度 | 优化方向 |
---|---|---|---|
遍历(DFS/层序) | O(n) | 递归时 O(h) 或队列 O(n) | 非递归实现减少递归深度风险 |
构建二叉树 | O(n) | O(n)(辅助存储中序索引) | 利用哈希表降低查找中序下标时间复杂度 |
注意: 树的遍历大部分都需要访问所有节点,时间复杂度保持在线性级别。
五、大厂真题实战
真题1:二叉树的最近公共祖先(LeetCode 236)
题目描述:
给定一个二叉树,找到两个指定节点的最近公共祖先
参考解法:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (root == nullptr || 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;
}
真题2:二叉树的层序遍历(LeetCode 102)
题目描述:
返回二叉树的层序遍历结果,每层节点从左到右依次输出
参考解法:
前文已提供基于队列的层序遍历实现,详见上文层序遍历示例。
六、常见误区与避坑指南
- 递归出口遗漏: 二叉树遍历与构建时,务必检查空节点条件
- 遍历顺序混淆: 记住前序、中序与后序的递归调用顺序,避免顺序错误
- 内存管理: 手动管理动态分配的节点,防止内存泄漏(使用智能指针可降低风险)
- 索引计算错误: 在利用数组构建完全二叉树时,注意左右子节点的下标计算公式(左:2i+1,右:2i+2)
七、总结与扩展
核心优势:
- 二叉树遍历和构建是解决树形结构问题的重要基础
- 多种遍历方式适用于不同场景,递归与迭代各有优势
- 利用哈希表辅助可优化构树与查找效率
扩展思考:
- 如何高效完成二叉搜索树的平衡(如 AVL、红黑树)?
- 二叉树如何应用在区间查询、优先级调度或 Huffman 编码等场景?
- 如何用非递归方法实现后序遍历,避免递归带来的栈溢出风险?