这道题目考察的是二叉树的基本操作和变形处理。给定一棵二叉树的中序遍历和前序遍历序列,要求我们首先重建这棵二叉树,然后输出其镜像后的层序遍历序列。这实际上包含了三个关键操作:根据遍历序列重建二叉树、对二叉树进行镜像反转、进行层序遍历输出。
二叉树的重建是基于前序遍历和中序遍历的特性完成的。前序遍历的第一个元素总是当前子树的根节点,而中序遍历中根节点的位置可以将序列分为左子树和右子树两部分。通过递归地应用这一原理,我们可以完整地重建整棵二叉树。
在实际操作中,为了提高效率,我们使用哈希表(unordered_map)来存储中序遍历中每个节点的位置,这样可以在O(1)时间内找到根节点在中序遍历中的位置,避免了每次线性查找的时间消耗。
题目要求的"反转"或"镜像"操作,实际上就是交换每个节点的左右子树。在代码实现上,我们不需要真正修改树的结构,只需要在层序遍历时改变入队顺序即可。常规的层序遍历是先左后右,而镜像后的层序遍历则是先右后左。
这种实现方式非常巧妙,它避免了显式地进行树结构的修改,而是通过遍历顺序的改变达到了同样的效果,既节省了时间又减少了空间的使用。
cpp复制#include<bits/stdc++.h>
#define lc p<<1 // 左孩子
#define rc (p<<1)+1 // 右孩子
using namespace std;
// 模拟二叉树存储结构
int tr[100010];
int a[5000], b[5000];
int idx = 1;
unordered_map<int, int> mp;
这里我们使用了数组来模拟二叉树结构,这是一种常见的空间优化方法。通过位运算(p<<1)来快速计算左右孩子的索引位置,这在处理完全二叉树时特别高效。哈希表mp用于存储中序遍历中各个节点的位置信息,以便快速查找。
cpp复制void f(int l, int r, int p) {
if(l > r) {
return;
}
// 前序遍历的当前元素为根节点
tr[p] = b[idx++];
// 获取根节点在中序序列中的位置
int md = mp[tr[p]];
// 递归构建左子树和右子树
f(l, md - 1, lc);
f(md + 1, r, rc);
}
这个递归函数是重建二叉树的核心。参数l和r定义了当前子树在中序遍历序列中的范围,p是当前节点在数组tr中的索引。每次递归调用都会处理一个子树,直到范围无效(l > r)时返回。
cpp复制queue<int> q;
q.push(1);
bool fl = 0; // 控制输出格式前的空格
while(q.size()){
int p = q.front();
q.pop();
// 如果当前节点为空,则跳过
if(tr[p] == 0) continue;
if(fl) cout << " ";
cout << tr[p];
fl = 1;
// 核心:因为要反转,所以先压入右孩子,再压入左孩子
q.push(rc);
q.push(lc);
}
这段代码实现了镜像层序遍历。关键点在于入队顺序的改变:先右孩子后左孩子。fl标志用于控制输出格式,确保第一个元素前没有多余的空格。
使用哈希表存储中序遍历的位置信息是本算法的一个关键优化点。如果没有这个优化,每次寻找根节点在中序遍历中的位置都需要O(n)的时间,整个算法的时间复杂度会上升到O(n²)。而使用哈希表后,每次查找都是O(1)时间,使得整体时间复杂度降为O(n)。
空间复杂度主要来自以下几个方面:
所有这些数据结构的大小都与输入规模n成正比,因此总的空间复杂度是O(n)。题目中数组大小适当放大是为了防止越界,但并不改变渐进复杂度。
在实现二叉树重建的递归函数时,必须正确处理边界条件。当l > r时,表示当前子树为空,应该立即返回。忽略这个边界条件会导致无限递归或数组越界等问题。
使用数组模拟二叉树时,索引管理非常重要。在这个实现中,根节点放在索引1的位置,左孩子是2p,右孩子是2p+1。这种安排使得父子节点之间的关系可以通过简单的位运算快速计算,但同时也需要注意数组大小要足够大,防止越界。
题目通常对输出格式有严格要求。在这个问题中,需要在元素之间用空格分隔,但第一个元素前不能有空格。使用fl标志来控制空格输出是一个简单有效的解决方案。
除了前序+中序的组合,二叉树还可以通过其他遍历组合来重建,例如:
每种组合的重建方法略有不同,但核心思想都是利用一种遍历确定根节点,另一种遍历划分左右子树。
虽然本题通过改变遍历顺序实现了镜像效果,但有时我们需要真正修改树的结构。这时可以在重建过程中交换左右子树的构建顺序,或者在构建完成后进行后序遍历交换每个节点的左右指针。
递归实现简洁易懂,但在处理深度很大的树时可能会有栈溢出的风险。可以考虑使用栈来模拟递归过程,实现非递归的二叉树重建和遍历算法。
二叉树及其遍历在计算机科学中有广泛应用,例如:
理解二叉树的基本操作和变形处理,是掌握更复杂数据结构和算法的基础。这道题目虽然看似简单,但涵盖了二叉树操作的核心概念,是很好的练习材料。