一、根据先序遍历和中序遍历恢复二叉树
思路:
根据先序遍历的第一个元素建立根结点;然后在中序列表中找到该元素,以确定根结点的左右子树的中序列;再在先序序列中确定左右子树的先序序列;最后由左子树的先序序列中确定左右子树的先序序列;最后由左子树的先序序列与中序序列建立左子树,由右子树的先序序列与中序序列建立左子树,由右子树的先序序列和中序序列建立右子树。
rebitree(char pread[],char incd[],bitree root)
{
if(n<=0) root=NULL;
else preinod(preod,1,n,inod,1,n,&root);
}
void preinod(char preod[],int i,int j,char inod[],int k,int h,botree *t)
{
*t=(bitnode *)malloc(sizeof(bitnode));
*t->data=preod[i]; // 先序序列的第一个值为二叉树的根
m=k;
while(inod[m]!=preod[i]) //在中序序列中寻找根结点
m++;
if(m==k) *t->lchild=NULL;
else preinod(pred,i+1,i+m-k,inod,k,m-1,&(*t-<lchild)); //生成左子树
if(m==h)
else preinod(preod,i+m-k+1,j,inod,m+1,h,&(*t->rchild)); //生成右子树
}
二、由中序和后序确定二叉树
//inorder[]:中序序列
//preorder[]:后序序列
TreeNode* traversal (int inorder[], int inorderBegin, int inorderEnd, int preorder[], int preorderBegin, int preorderEnd)
{
if (preorderBegin == preorderEnd) return NULL;
int rootValue = preorder[preorderBegin]; // 注意用preorderBegin 不要用0
TreeNode* root ;
root->data=rootvalue;
if (preorderEnd - preorderBegin == 1) return root;
int delimiterIndex;
for (delimiterIndex = inorderBegin; delimiterIndex < inorderEnd; delimiterIndex++) {
if (inorder[delimiterIndex] == rootValue) break;
}
// 切割中序数组
// 中序左区间,左闭右开[leftInorderBegin, leftInorderEnd)
int leftInorderBegin = inorderBegin;
int leftInorderEnd = delimiterIndex;
// 中序右区间,左闭右开[rightInorderBegin, rightInorderEnd)
int rightInorderBegin = delimiterIndex + 1;
int rightInorderEnd = inorderEnd;
// 切割前序数组
// 前序左区间,左闭右开[leftPreorderBegin, leftPreorderEnd)
int leftPreorderBegin = preorderBegin + 1;
int leftPreorderEnd = preorderBegin + 1 + delimiterIndex - inorderBegin; // 终止位置是起始位置加上中序左区间的大小size
// 前序右区间, 左闭右开[rightPreorderBegin, rightPreorderEnd)
int rightPreorderBegin = preorderBegin + 1 + (delimiterIndex - inorderBegin);
int rightPreorderEnd = preorderEnd;
root->left = traversal(inorder, leftInorderBegin, leftInorderEnd, preorder, leftPreorderBegin, leftPreorderEnd);
root->right = traversal(inorder, rightInorderBegin, rightInorderEnd, preorder, rightPreorderBegin, rightPreorderEnd);
return root;
}
TreeNode * buildTree(int preorder[], int inorder[])
{
if (strlen(inorder)== 0 || strlen(preorder)== 0)
return NULL;
// 参数坚持左闭右开的原则
return traversal(inorder, 0, strlen(inorder), preorder, 0, strlen(preorder));
}
[USACO3.4] 美国血统 American Heritage
题目描述
农夫约翰非常认真地对待他的奶牛们的血统。然而他不是一个真正优秀的记帐员。他把他的奶牛 们的家谱作成二叉树,并且把二叉树以更线性的“树的中序遍历”和“树的前序遍历”的符号加以记录而 不是用图形的方法。
你的任务是在被给予奶牛家谱的“树中序遍历”和“树前序遍历”的符号后,创建奶牛家谱的“树的 后序遍历”的符号。每一头奶牛的姓名被译为一个唯一的字母。(你可能已经知道你可以在知道树的两 种遍历以后可以经常地重建这棵树。)显然,这里的树不会有多于 26 个的顶点。 这是在样例输入和 样例输出中的树的图形表达方式:
C
/ \
/ \
B G
/ \ /
A D H
/ \
E F
树的中序遍历是按照左子树,根,右子树的顺序访问节点。
树的前序遍历是按照根,左子树,右子树的顺序访问节点。
树的后序遍历是按照左子树,右子树,根的顺序访问节点。
输入格式
第一行: 树的中序遍历
第二行: 同样的树的前序遍历
输出格式
单独的一行表示该树的后序遍历。
样例输入
ABEDFCHG
CBADEFGH
样例输出
AEFDBHGC
提示
题目翻译来自NOCOW。
USACO Training Section 3.4
思路:
根据二叉树的先序遍历(根结点,左子树,右子树)我可以知道 A 是根结点,再在中序序列中找到根结点,进行分割,中序遍历(左子树,根结点,右子树),找到根节点后,可以按照这个遍历顺序,找到左子树的部分和右子树的部分。再依据左子树的个数和右子树的结点个数,划分先序序列的左子树和右子树,输出的时候以后序遍历的顺序输出(左子树,右子树,根结点)。
#include<stdio.h>
#include<string.h>
typedef struct tree
{
char data;
struct tree *left;
struct tree *right;
}node;
char in[100],or[100];
void traversal(int inb,int ine,int orb,int ore)
{
int i;
for(i=inb;i<=ine;i++)
{
if(in[i]==or[orb]) //查找中序序列根结点,然后进行分割
{ traversal(inb,i,orb+1,orb+i-inb); //先序序列进行左子树复原的时候记得去除根结点,根结点在第一个位置
traversal(i+1,ine,orb+1+i-inb,ore); //恢复右子树
printf("%c",in[i]);
}
}
}
int main()
{
scanf("%s",in);
scanf("%s",or);
traversal(0,strlen(in)-1,0,strlen(or)-1);
return 0;
}