PTA:玩转二叉树
给定一棵二叉树的中序遍历和前序遍历,请你先将树做个镜面反转,再输出反转后的层序遍历的序列。所谓镜面反转,是指将所有非叶结点的左右孩子对换。这里假设键值都是互不相等的正整数。
输入格式:
输入第一行给出一个正整数N(≤30),是二叉树中结点的个数。第二行给出其中序遍历序列。第三行给出其前序遍历序列。数字间以空格分隔。
输出格式:
在一行中输出该树反转后的层序遍历的序列。数字间以1个空格分隔,行首尾不得有多余空格。
输入样例:
7
1 2 3 4 5 6 7
4 1 3 2 6 5 7
输出样例:
4 6 1 7 5 3 2
代码如下:
#include<iostream>
#include<queue>
using namespace std;
typedef struct BiTNode
{
int data;
struct BiTNode *lchild,*rchild;
} BiTNode,*BiTree;
BiTree buildTree(int *inorder,int *preorder,int inStart,int inEnd,int preStart,int preEnd)
{
if(inStart>inEnd||preStart>preEnd)
{
return nullptr;
}
int rootValue=preorder[preStart];
BiTree root=new BiTNode;
root->data=rootValue;
root->lchild=root->rchild=nullptr;
int rootIndexInorder;
for(int i=inStart;i<=inEnd;i++)
{
if(inorder[i]==rootValue)
{
rootIndexInorder=i;
break;
}
}
int leftSubtreeSize=rootIndexInorder-inStart;
root->lchild=buildTree(inorder,preorder,inStart,rootIndexInorder-1,preStart+1,preStart+leftSubtreeSize);
root->rchild=buildTree(inorder,preorder,rootIndexInorder+1, inEnd, preStart+leftSubtreeSize+1,preEnd);
return root;
}
//镜面反转二叉树
void mirrorTree(BiTree root)
{
if(root==nullptr)
{
return;
}
//交换左右子树
BiTree temp=root->lchild;
root->lchild=root->rchild;
root->rchild=temp;
//递归反转左右子树
mirrorTree(root->lchild);
mirrorTree(root->rchild);
}
//层序遍历二叉树
void levelOrderTraversal(BiTree root)
{
if(root==nullptr){
return;
}
queue<BiTree> q;
q.push(root);
bool first=true;
while (!q.empty()){
BiTree current=q.front();
q.pop();
if(!first){
cout<< " ";
}
cout<<current->data;
first=false;
if(current->lchild!=nullptr){
q.push(current->lchild);
}
if(current->rchild!=nullptr)
{
q.push(current->rchild);
}
}
}
int main()
{
int n;
cin>>n;
int *inorder=new int[n];
int *preorder=new int[n];
for(int i=0;i<n;i++)
{
cin>>inorder[i];
}
for(int i=0;i<n;i++)
{
cin>>preorder[i];
}
BiTree root=buildTree(inorder,preorder,0,n-1,0,n-1);
mirrorTree(root);
levelOrderTraversal(root);
delete[] inorder;
delete[] preorder;
return 0;
}
之前做的有关树的题还没有关于构造树的题,这道题含的考点比较多,就拿这个题来细说吧。
buildTree 函数:
利用前序遍历和中序遍历的特点,通过递归的方式来逐步构建出二叉树。
BiTree buildTree(int *inorder, int *preorder, int inStart, int inEnd, int preStart, int preEnd)
{
// 1. 边界条件检查
if (inStart > inEnd || preStart > preEnd) {
return nullptr;
}
// 2. 确定当前子树的根节点值
int rootValue = preorder[preStart];
BiTree root = new BiTNode;
root->data = rootValue;
root->lchild = root->rchild = nullptr;
// 3. 在中序遍历序列中找到根节点的位置
int rootIndexInorder;
for (int i = inStart; i <= inEnd; i++) {
if (inorder[i] == rootValue) {
rootIndexInorder = i;
break;
}
}
// 4. 计算左子树的节点数量
int leftSubtreeSize = rootIndexInorder - inStart;
// 5. 递归构建左子树
root->lchild = buildTree(inorder, preorder, inStart, rootIndexInorder - 1, preStart + 1, preStart + leftSubtreeSize);
// 6. 递归构建右子树
root->rchild = buildTree(inorder, preorder, rootIndexInorder + 1, inEnd, preStart + leftSubtreeSize + 1, preEnd);
return root;
}
函数原型和参数
BiTree buildTree(int *inorder, int *preorder, int inStart, int inEnd, int preStart, int preEnd);
inorder:存储二叉树中序遍历结果的数组。
preorder:存储二叉树前序遍历结果的数组。
inStart 和 inEnd:表示当前处理的中序遍历序列的起始和结束索引。
preStart 和 preEnd:表示当前处理的前序遍历序列的起始和结束索引。
1. 边界条件检查
if (inStart > inEnd || preStart > preEnd) {
return nullptr;
}
当 inStart > inEnd 或者 preStart > preEnd 时,意味着当前处理的序列为空,也就是没有节点可以用来构建子树了,所以返回 nullptr。
2. 确定当前子树的根节点值
int rootValue = preorder[preStart];
BiTree root = new BiTNode;
root->data = rootValue;
root->lchild = root->rchild = nullptr;
前序遍历的特点是:根节点 -> 左子树 -> 右子树。所以 preorder 数组的第一个元素 preorder[preStart] 就是当前子树的根节点的值。
创建一个新的二叉树节点 root,将根节点的值赋给它,并将其左右子节点指针初始化为 nullptr。
3. 在中序遍历序列中找到根节点的位置
int rootIndexInorder;
for (int i = inStart; i <= inEnd; i++) {
if (inorder[i] == rootValue) {
rootIndexInorder = i;
break;
}
}
中序遍历的特点是:左子树 -> 根节点 -> 右子树。通过遍历中序遍历序列 inorder,找到根节点值所在的索引 rootIndexInorder。这个位置将中序遍历序列划分为左子树的中序遍历序列和右子树的中序遍历序列两部分。
4. 计算左子树的节点数量
int leftSubtreeSize = rootIndexInorder - inStart;
左子树的节点数量等于根节点在中序遍历序列中的索引减去中序遍历序列的起始索引。这个数量可以帮助我们确定左右子树在前序遍历序列中的范围。
5. 递归构建左子树
root->lchild = buildTree(inorder, preorder, inStart, rootIndexInorder - 1, preStart + 1, preStart + leftSubtreeSize);
中序遍历序列范围:左子树的中序遍历序列从 inStart 到 rootIndexInorder - 1。
前序遍历序列范围:左子树的前序遍历序列从 preStart + 1 到 preStart + leftSubtreeSize。因为前序遍历中根节点之后紧跟着的就是左子树的节点。
递归调用 buildTree 函数构建左子树,并将返回的左子树的根节点指针赋给当前根节点的 lchild。
6. 递归构建右子树
root->rchild = buildTree(inorder, preorder, rootIndexInorder + 1, inEnd, preStart + leftSubtreeSize + 1, preEnd);
中序遍历序列范围:右子树的中序遍历序列从 rootIndexInorder + 1 到 inEnd。
前序遍历序列范围:右子树的前序遍历序列从 preStart + leftSubtreeSize + 1 到 preEnd。
递归调用 buildTree 函数构建右子树,并将返回的右子树的根节点指针赋给当前根节点的 rchild。
mirrorTree 函数
对二叉树进行镜面反转,即交换每个非叶子节点的左右子树。
void mirrorTree(BiTree root)
{
if (root == nullptr) {
return;
}
BiTree temp = root->lchild;
root->lchild = root->rchild;
root->rchild = temp;
mirrorTree(root->lchild);
mirrorTree(root->rchild);
}
参数:root 为要反转的二叉树的根节点。
实现步骤:
- 边界条件检查:如果根节点为空,直接返回。
- 交换左右子树:使用临时指针 temp 交换当前节点的左右子树。
- 递归反转左右子树:对左右子树分别递归调用 mirrorTree 函数。
levelOrderTraversal 函数
对二叉树进行层序遍历并输出节点值。
void levelOrderTraversal(BiTree root)
{
if (root == nullptr) {
return;
}
queue<BiTree> q;
q.push(root);
bool first = true;
while (!q.empty()) {
BiTree current = q.front();
q.pop();
if (!first) {
cout << " ";
}
cout << current->data;
first = false;
if (current->lchild != nullptr) {
q.push(current->lchild);
}
if (current->rchild != nullptr) {
q.push(current->rchild);
}
}
}
参数:root 为要遍历的二叉树的根节点。
实现步骤:
1)边界条件检查:如果根节点为空,直接返回。
2)初始化队列:创建一个队列 q,将根节点入队。
3)层序遍历:
- 当队列不为空时,取出队首节点 current。
- 如果不是第一个输出的节点,先输出一个空格。
- 输出当前节点的值。
- 如果当前节点的左子节点不为空,将左子节点入队。
- 如果当前节点的右子节点不为空,将右子节点入队。
更多推荐



所有评论(0)