题目1385:重建二叉树
时间限制:1 秒
内存限制:32 兆
特殊判题:否
提交:1701
解决:553
题目描述:
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并输出它的后序遍历序列。
输入:
输入可能包含多个测试样例,对于每个测试案例,
输入的第一行为一个整数n(1<=n<=1000):代表二叉树的节点个数。
输入的第二行包括n个整数(其中每个元素a的范围为(1<=a<=1000)):代表二叉树的前序遍历序列。
输入的第三行包括n个整数(其中每个元素a的范围为(1<=a<=1000)):代表二叉树的中序遍历序列。
输出:
对应每个测试案例,输出一行:
如果题目中所给的前序和中序遍历序列能构成一棵二叉树,则输出n个整数,代表二叉树的后序遍历序列,每个元素后面都有空格。
如果题目中所给的前序和中序遍历序列不能构成一棵二叉树,则输出”No”。
样例输入:
8
1 2 4 7 3 5 6 8
4 7 2 1 5 3 8 6
8
1 2 4 7 3 5 6 8
4 1 2 7 5 3 8 6
样例输出:
7 4 2 5 8 6 3 1
No
题解
这是个典型的题目,记得上数据结构课的时候老师要求上机实习做,当时也做了,不过用的方法相当笨拙,想在自己也搞不懂,贴出来看看,不由得窃笑一年半前的自己。现在看来那时的代码过于形式化且存在严重的逻辑错误。
#include<stdio.h> #include<stdlib.h> #include<malloc.h> #define MaxSize 100 typedef char DataType; typedef struct { DataType data; int tag; }string; typedef struct Node { DataType data; struct Node *leftChild; struct Node *rightChild; }BiTreeNode; //判断左子树是否为空,若空返回0,,否则返回1 int LeftJudge(string *Intree,DataType ch,int n) { int i=0,count=0; for(i=0;Intree[i].data!=ch/*&&i<n*/;i++) { if(Intree[i].tag==0) count=1; } Intree[i].tag=1; if(count==1)return 1; return 0; } //判断右子树是否为空,若空返回0,,否则返回1 int RightJudge(string *Intree,DataType ch,int n) { int i; for(i=0;Intree[i].data!=ch&&i<n-1;i++); if(Intree[i+1].tag==0) { Intree[i].tag=1; return 1; } return 0; } //创建以bt为根结点的二叉树 void CreatBiTree(BiTreeNode *bt,string *Intree,string *Pretree,int n) { int i; bt->leftChild=(BiTreeNode *)malloc(sizeof(BiTreeNode)); bt->rightChild=(BiTreeNode *)malloc(sizeof(BiTreeNode)); for(i=0;i<n&&Pretree[i].tag!=0;i++); if(Pretree[i].tag==0) { bt->data=Pretree[i].data; // printf("Pretree[%d].data=%c,tag=%d,i=%d\n",i,bt->data,Pretree[i].tag,i); Pretree[i].tag=1; if(LeftJudge(Intree,Pretree[i].data,n)!=0) { CreatBiTree(bt->leftChild,Intree,Pretree,n); } else { bt->leftChild=NULL; //printf("左 "); } if(RightJudge(Intree,Pretree[i].data,n)!=0) { CreatBiTree(bt->rightChild,Intree,Pretree,n); } else { bt->rightChild=NULL; // printf("右 "); } } } //前序遍历以bt为根结点指针的二叉树,并将前序序列存入CrePretree所指向数组中 void PreOrder(BiTreeNode *bt,DataType *CrePretree,int *i) { if (bt==NULL) return; printf("%c",bt->data); CrePretree[(*i)++]=bt->data; PreOrder(bt->leftChild,CrePretree,i); PreOrder(bt->rightChild,CrePretree,i); } //中序遍历以bt为根结点指针的二叉树,并将中序序列存入CreIntree所指向数组中 void InOrder(BiTreeNode *bt,DataType *CreIntree,int *i) { if (bt==NULL) return; InOrder(bt->leftChild,CreIntree,i); printf("%c",bt->data); CreIntree[(*i)++]=bt->data; InOrder(bt->rightChild,CreIntree,i); } //凹入形式遍历二叉树 void PrintBiTree(BiTreeNode *root,int n) { int i; if(root==NULL)return ; PrintBiTree(root->leftChild,n+1); for(i=0;i<n-1;i++)printf(" "); if(n>0) { printf("-----"); printf("%c\n",root->data); } PrintBiTree(root->rightChild,n+1); } //输入二叉树的前序和中序序列,并初步判断能否构成完整二叉树 void StringInput(string *Pretree,string *Intree,int *n) { int i,j,k; start: printf("请输入二叉树的结点个数:"); scanf("%d",n); getchar(); if(*n==-1)exit(0);//如果n=-1退出 printf("请输入二叉树的前序遍历序列:"); for(i=0;i<*n;i++) { scanf("%c",&Pretree[i].data); Pretree[i].tag=0; } getchar(); printf("请输入二叉树的中序遍历序列:"); for(i=0;i<*n;i++) { scanf("%c",&Intree[i].data); Intree[i].tag=0; } getchar(); for(i=0;i<*n;i++) { k=0; for(j=0;j<*n;j++) { if(Pretree[i].data==Intree[j].data) k++; } if(k!=1) { printf("输入序列有问题!!!\n请重新输入:\n"); goto start; } } } int main() { int n,i,rank=0; string Pretree[MaxSize];//二叉树的前序遍历序列 string Intree[MaxSize]; DataType CrePretree[MaxSize],CreIntree[MaxSize]; BiTreeNode *root=(BiTreeNode *)malloc(sizeof(BiTreeNode)); getchar(); printf("***************************************************************\n"); do { rank++; printf("第%d组数据:\n",rank); StringInput(Pretree,Intree,&n); CreatBiTree(root,Intree,Pretree,n); printf("\n凹入形式遍历构造二叉树:\n"); PrintBiTree(root,1); printf("证明构造是否正确:\n"); i=0; PreOrder(root,CrePretree,&i); for(i=0;i<n;i++) { if(CrePretree[i]!=Pretree[i].data) { printf("前序遍历不符,创建不正确!\n"); break; } } printf("\n"); if(i==n)printf("前序遍历创建正确!\n"); i=0; InOrder(root,CreIntree,&i); for(i=0;i<n;i++) { if(CreIntree[i]!=Intree[i].data) { printf("中序遍历不符,创建不正确!\n"); printf("即输入的前序和中序不能创建一二叉树!\n"); break; } } printf("\n"); if(i==n)printf("中序遍历创建正确!\n"); getchar(); printf("***************************************************************\n"); }while(1); return 0; }
二叉树的遍历可以写成递归的形式,于是本题完全可以结合二叉树的特点递归解题。读入前序数组中的一个元素,把中序分成左右两部分并根据匹配中序的相对位置把前序分成左右两部分;然后再递归进入下一层。时间复杂度在最坏情况下为O(n^2),最好情况为O(n*log(n))(在二叉树为完全二叉树时)。
#include<iostream> #include<cstdio> using namespace std; const int MAXN=1000+10; int preArray[MAXN],curArray[MAXN],sufArray[MAXN]; bool solve(int &id,int preNow,int curStart,int curEnd) { if(curStart>=curEnd) return true; int i; for(i=curStart;i<curEnd;i++) { if(curArray[i]==preArray[preNow])break; } if(i>=curEnd)return false; bool flag1=solve(id,preNow+1,curStart,i); bool flag2=solve(id,preNow+i-curStart+1,i+1,curEnd); if(!flag1||!flag2)return false; sufArray[id++]=preArray[preNow]; return true; } int main() { int n; while(~scanf("%d",&n)) { int i; for(i=0;i<n;i++) scanf("%d",&preArray[i]); for(i=0;i<n;i++) scanf("%d",&curArray[i]); int id=0; if(solve(id,0,0,n)) { for(i=0;i<n;i++) printf("%d ",sufArray[i]); printf("\n"); } else printf("No\n"); } return 0; }
能力有限,难免会有错误或不足,望斧正!