1. 二叉树基础概念与遍历原理
二叉树是计算机科学中最基础也是最重要的数据结构之一。它由节点组成,每个节点最多有两个子节点,分别称为左子节点和右子节点。二叉树在算法设计中应用广泛,从文件系统到数据库索引,从编译器设计到机器学习算法,都能看到它的身影。
1.1 二叉树的三种基本遍历方式
**前序遍历(Pre-order Traversal)**遵循"根-左-右"的顺序:
- 首先访问根节点
- 然后递归遍历左子树
- 最后递归遍历右子树
前序遍历的一个典型应用场景是复制一棵树的结构,因为根节点总是第一个被访问的。
**中序遍历(In-order Traversal)**采用"左-根-右"的顺序:
- 先递归遍历左子树
- 然后访问根节点
- 最后递归遍历右子树
对于二叉搜索树(BST),中序遍历会得到一个升序排列的序列,这是中序遍历最显著的特性。
**后序遍历(Post-order Traversal)**按照"左-右-根"的顺序:
- 先递归遍历左子树
- 然后递归遍历右子树
- 最后访问根节点
后序遍历常用于删除树节点或计算表达式树的值,因为子节点总是在父节点之前被处理。
1.2 遍历的递归实现原理
递归实现是理解二叉树遍历最直观的方式。以中序遍历为例:
cpp复制void inOrder(TreeNode *root) {
if(root == NULL) return; // 递归终止条件
inOrder(root->left); // 遍历左子树
cout << root->data << " "; // 访问当前节点
inOrder(root->right); // 遍历右子树
}
递归调用的本质是利用函数调用栈来保存遍历的路径。当遇到空节点(NULL)时,递归开始回溯,完成整个遍历过程。
注意:递归实现虽然简洁,但在处理深度很大的树时可能导致栈溢出。在实际工程中,对于深度不确定的树结构,建议使用非递归的迭代实现。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 根据遍历序列重建二叉树
2.1 前序+中序重建二叉树
给定一棵二叉树的前序遍历和中序遍历序列,可以唯一确定这棵树的结构。这是二叉树算法中的一个经典问题。
重建算法步骤:
- 前序遍历的第一个元素是树的根节点
- 在中序遍历中找到这个根节点,它将序列分为左子树和右子树的中序遍历
- 根据左子树的长度,在前序遍历中确定左右子树的前序遍历
- 递归重建左右子树
cpp复制TreeNode* buildTree(string pre, string in) {
if(pre.empty()) return NULL;
char rootVal = pre[0];
int rootPos = in.find(rootVal);
TreeNode* root = new TreeNode(rootVal);
root->left = buildTree(pre.substr(1, rootPos), in.substr(0, rootPos));
root->right = buildTree(pre.substr(rootPos+1), in.substr(rootPos+1));
return root;
}
2.2 后序+中序重建二叉树
类似地,给定后序和中序遍历序列也能重建二叉树,只是根节点位于后序遍历的最后一个位置。
关键点:
- 后序遍历的最后一个元素是根节点
- 中序遍历中根节点的位置划分左右子树
- 递归构建时需要注意子串的截取范围
2.3 层序遍历的特殊处理
层序遍历(Level-order Traversal)按照树的层级从上到下、从左到右访问节点。它需要使用队列作为辅助数据结构:
cpp复制void levelOrder(TreeNode* root) {
if(root == NULL) return;
queue<TreeNode*> q;
q.push(root);
while(!q.empty()) {
TreeNode* current = q.front();
q.pop();
cout << curre
