这道题考察的是二叉树的基本操作和遍历技巧。我们先明确几个关键概念:二叉树是一种每个节点最多有两个子节点的树结构,通常称为左子树和右子树。题目中的"玩转"二字暗示我们需要对二叉树进行某种变形或特殊处理。
在实际编程竞赛和面试中,二叉树相关题目出现频率极高。根据我的经验,掌握二叉树的几种遍历方式(前序、中序、后序、层次)是解决这类问题的基础。这道题的特殊之处在于它要求我们"玩转"二叉树,也就是需要对二叉树进行镜像反转操作。
注意:在C++中处理二叉树时,指针操作要格外小心,内存泄漏和空指针访问是常见错误源。
题目通常会给出二叉树的前序遍历和中序遍历序列,要求输出某种遍历结果。对于"玩转"操作,一般是指生成原二叉树的镜像,然后输出其层次遍历结果。
输入示例:
code复制前序序列:1 2 3 4 5 6 7
中序序列:2 3 1 5 4 7 6
预期输出应该是镜像二叉树的层次遍历结果。理解这一点是解题的关键。
解决这个问题需要两个主要步骤:
重建二叉树是经典算法问题,时间复杂度为O(n^2)(最坏情况)或O(nlogn)(平衡二叉树情况)。生成镜像可以使用递归或迭代方式,层次遍历则需要借助队列数据结构。
首先定义二叉树节点结构:
cpp复制struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
这个简单的结构包含了节点值和左右子节点指针。构造函数初始化节点值并将指针置空,这是良好的编程习惯。
重建二叉树的递归函数:
cpp复制TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder,
int preStart, int inStart, int inEnd) {
if (preStart >= preorder.size() || inStart > inEnd) {
return nullptr;
}
TreeNode* root = new TreeNode(preorder[preStart]);
int inIndex = 0;
for (int i = inStart; i <= inEnd; i++) {
if (inorder[i] == root->val) {
inIndex = i;
break;
}
}
root->left = buildTree(preorder, inorder, preStart + 1, inStart, inIndex - 1);
root->right = buildTree(preorder, inorder, preStart + (inIndex - inStart) + 1, inIndex + 1, inEnd);
return root;
}
这个函数通过前序序列确定根节点,然后在中序序列中找到根节点位置,将序列分为左右子树部分,递归构建整棵树。
镜像生成的递归实现:
cpp复制void mirrorTree(TreeNode* root) {
if (!root) return;
// 交换左右子树
TreeNode* temp = root->left;
root->left = root->right;
root->right = temp;
// 递归处理子树
mirrorTree(root->left);
mirrorTree(root->right);
}
这个简洁的递归函数通过交换每个节点的左右子树来实现镜像效果。注意递归终止条件是遇到空指针。
层次遍历使用队列辅助:
cpp复制vector<int> levelOrder(TreeNode* root) {
vector<int> result;
if (!root) return result;
queue<TreeNode*> q;
q.push(root);
while (!q.empty()) {
TreeNode* node = q.front();
q.pop();
result.push_back(node->val);
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
return result;
}
队列保证了节点按照层级顺序被处理,先入队的节点先被访问,符合广度优先搜索的原则。
将上述各部分组合起来:
cpp复制#include <iostream>
#include <vector>
#include <queue>
using namespace std;
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder,
int preStart, int inStart, int inEnd) {
if (preStart >= preorder.size() || inStart > inEnd) {
return nullptr;
}
TreeNode* root = new TreeNode(preorder[preStart]);
int inIndex = 0;
for (int i = inStart; i <= inEnd; i++) {
if (inorder[i] == root->val) {
inIndex = i;
break;
}
}
root->left = buildTree(preorder, inorder, preStart + 1, inStart, inIndex - 1);
root->right = buildTree(preorder, inorder, preStart + (inIndex - inStart) + 1, inIndex + 1, inEnd);
return root;
}
void mirrorTree(TreeNode* root) {
if (!root) return;
TreeNode* temp = root->left;
root->left = root->right;
root->right = temp;
mirrorTree(root->left);
mirrorTree(root->right);
}
vector<int> levelOrder(TreeNode* root) {
vector<int> result;
if (!root) return result;
queue<TreeNode*> q;
q.push(root);
while (!q.empty()) {
TreeNode* node = q.front();
q.pop();
result.push_back(node->val);
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
return result;
}
int main() {
int n;
cin >> n;
vector<int> preorder(n), inorder(n);
for (int i = 0; i < n; i++) cin >> preorder[i];
for (int i = 0; i < n; i++) cin >> inorder[i];
TreeNode* root = buildTree(preorder, inorder, 0, 0, n - 1);
mirrorTree(root);
vector<int> result = levelOrder(root);
for (int i = 0; i < result.size(); i++) {
if (i != 0) cout << " ";
cout << result[i];
}
return 0;
}
整体复杂度主要由重建二叉树的过程决定。可以通过哈希表优化中序序列的查找过程,将重建复杂度降到O(n)。
使用哈希表存储中序序列的位置:
cpp复制TreeNode* buildTreeOpt(vector<int>& preorder, vector<int>& inorder,
int preStart, int inStart, int inEnd,
unordered_map<int, int>& indexMap) {
if (preStart >= preorder.size() || inStart > inEnd) {
return nullptr;
}
TreeNode* root = new TreeNode(preorder[preStart]);
int inIndex = indexMap[root->val];
root->left = buildTreeOpt(preorder, inorder, preStart + 1, inStart, inIndex - 1, indexMap);
root->right = buildTreeOpt(preorder, inorder, preStart + (inIndex - inStart) + 1, inIndex + 1, inEnd, indexMap);
return root;
}
在主函数中预先构建哈希表:
cpp复制unordered_map<int, int> indexMap;
for (int i = 0; i < n; i++) {
indexMap[inorder[i]] = i;
}
TreeNode* root = buildTreeOpt(preorder, inorder, 0, 0, n - 1, indexMap);
需要特别注意以下几种情况:
在实际编程竞赛中,题目通常会保证输入的有效性,但在面试时可能需要考虑这些边界情况。
调试二叉树问题时,可以添加一个打印树的辅助函数,帮助可视化当前树结构。
有效测试用例应包括:
例如:
code复制测试用例1:
输入:
7
1 2 3 4 5 6 7
2 3 1 5 4 7 6
预期输出:1 6 4 7 5 3 2
测试用例2:
输入:
3
1 2 3
3 2 1
预期输出:1 2 3
递归实现简洁但可能有栈溢出风险,以下是镜像生成的迭代版本:
cpp复制void mirrorTreeIterative(TreeNode* root) {
if (!root) return;
stack<TreeNode*> s;
s.push(root);
while (!s.empty()) {
TreeNode* node = s.top();
s.pop();
swap(node->left, node->right);
if (node->left) s.push(node->left);
if (node->right) s.push(node->right);
}
}
二叉树镜像操作在实际中有多种应用:
掌握这道题的解法后,这些类似题目都可以迎刃而解。关键在于理解二叉树的基本操作和遍历方式。