已知树的中序遍历与前(后)序遍历求二叉树问题

PTA中三道已知树的中序遍历与前(后)序遍历求二叉树问题的链接:

  1. 给定一棵二叉树的后序遍历和中序遍历,请你输出其层序遍历的序列。这里假设键值都是互不相等的正整数。
  2. 给定一棵二叉树的中序遍历和前序遍历,请你先将树做个镜面反转,再输出反转后的层序遍历的序列。所谓镜面反转,是指将所有非叶结点的左右孩子对换。这里假设键值都是互不相等的正整数。
  3. 给定一棵二叉树的先序遍历序列和中序遍历序列,要求计算该二叉树的高度。

目前我们给出了三道pta中的习题,通过这三道题我们回顾一下已知树的中序遍历与前(后)序遍历求二叉树的相关问题。

首先我们来看第一道题:
给定一棵二叉树的后序遍历和中序遍历,请你输出其层序遍历的序列。这里假设键值都是互不相等的正整数。

输入格式:
输入第一行给出一个正整数N(≤30),是二叉树中结点的个数。第二行给出其后序遍历序列。第三行给出其中序遍历序列。数字间以空格分隔。

输出格式:
在一行中输出该树的层序遍历的序列。数字间以1个空格分隔,行首尾不得有多余空格。

输入样例:
7
2 3 1 5 7 6 4
1 2 3 4 5 6 7

输出样例:
4 1 6 3 5 7 2

我们首先需要知道,后序遍历的最后一个结点就是二叉树的根结点。而中序遍历中,根结点左边的就是左子树,右边的就是右子树。所以我们很快就可以从输入样例中得出4是根结点,123是左子树的结点,共3个结点,567是右子树的结点,共三个结点。
我们又知道,后序遍历先遍历左子树,后遍历右子树,所以根结点前面的3个结点576是右子树上的,剩下的231是左子树上的。这时就是一个递归定义的内容了,我们可以看出1是左子树的根结点,6是右子树的根结点…
这时我们已经给出了如何根据后序遍历和中序遍历求二叉树的思想,因为本人知识水平有限,就不再过多阐述。
下面附上这道题的代码:

#include <stdio.h>
#include <stdlib.h>
typedef struct node* bintree;
typedef struct node{
	bintree left;
	bintree right;
	int data;
}tree;
bintree creat(int n,int a[],int b[]){
	bintree p;
	int i;
	if(n==0){
		return NULL;//如果需要构造的二叉树结点数为0,返回NULL
	}else{//否则开始构造二叉树
		p=(bintree)malloc(sizeof(tree));//为结点申请空间
		p->data=a[n-1];//将后序遍历数组最后一个数的值赋给根结点
		for(i=0;i<n;i++){
			if(a[n-1]==b[i]){//找到根结点在中序遍历的位置
				break;
			}
		}
//左子树共有i个结点,右子树共有n-1-i个结点。
//左子树的后序遍历数组为last[0]-[i-1],中序遍历数组为mid[0]-[i-1]
//右子树的后序遍历数组为last[i]-[n-1],中序遍历数组为mid[i+1]-[n-1]
		p->left=creat(i,a,b);
		p->right=creat(n-1-i,a+i,b+i+1);
	}
	return p;
}
void pp(bintree bt){
	int front=0,rear=1;
	bintree p=bt,t[60];
	//这块队列的数组尽量开大一点,因为本身数据量不大,我们就偷个懒
	t[0]=bt;
	while(front<rear){
		if(t[front]!=NULL){
			p=t[front];
			if(p==bt){
				printf("%d",t[front]->data);
			}else{//此处注意输出格式
				printf(" %d",t[front]->data);
			}
			t[rear++]=p->left;
			t[rear++]=p->right;
		}
		front++;
	}//借用队列实现层序遍历
}
int main(){
	bintree a;
	int n,last[30],mid[30],i,j;
	scanf("%d",&n); 
	for(i=0;i<n;i++){
		scanf("%d",&last[i]);
	}
	for(i=0;i<n;i++){
		scanf("%d",&mid[i]);
	}
	a=creat(n,last,mid);
	pp(a);
	return 0;
} 

接着我们来看第二道题:
给定一棵二叉树的中序遍历和前序遍历,请你先将树做个镜面反转,再输出反转后的层序遍历的序列。所谓镜面反转,是指将所有非叶结点的左右孩子对换。这里假设键值都是互不相等的正整数。

输入格式:
输入第一行给出一个正整数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

和上面的题目是一个意思,我们先给出几个已知结论:
前序遍历数组的第一个数是根结点的值,在中序遍历中找到根结点所在位置i后我们就知道左右子树的结点数分别是 i 和 n-1-i。那么前序遍历数组pre[1]-[i]就是左子树的前序遍历,前序遍历数组pre[i+1]-[n-1]就是右子树的前序遍历。我们自然可以得知pre[1]是左子树的根结点,pre[i+1]是右子树的根结点,这也是个用递归可以解决的问题。
下面附上代码:

#include <stdio.h>
#include <stdlib.h>
typedef struct node* bintree;
typedef struct node{
	bintree left;
	bintree right;
	int data;
}tree;
bintree create(int n,int *pre,int *mid){
	//a前序遍历 b中序遍历 
	bintree p;
	int i;
	if(n==0){
		return NULL;
	}else{
		p=(bintree)malloc(sizeof(tree));
		p->data=pre[0];
		for(i=0;i<n;i++){
			if(mid[i]==pre[0]){
				break;
			}
		}
		p->left=create(i,pre+1,mid);
		p->right=create(n-1-i,pre+1+i,mid+i+1);
	}
	return p;
}
void jx(bintree bt){//这里的镜像也是一个很简单的递归函数
	bintree p;
	if(bt!=NULL){
		if(bt->left!=NULL || bt->right!=NULL){
			p=bt->right;
			bt->right=bt->left;
			bt->left=p;
		}
	}else{
		return;
	}
	jx(bt->left);
	jx(bt->right);
}
void pp(bintree bt){
	int front=0,rear=1;
	bintree t[60];
	t[0]=bt;
	while(front<rear){
		if(t[front]!=NULL){
			if(t[front]==bt){
				printf("%d",t[front]->data);
			}else{
				printf(" %d",t[front]->data);
			}
			t[rear++]=t[front]->left;
			t[rear++]=t[front]->right;
		}
		front++;
	}
}
int main(){
	int n,pre[30],mid[30],i;
	scanf("%d",&n);
	for(i=0;i<n;i++){
		scanf("%d",&mid[i]);
	}
	for(i=0;i<n;i++){
		scanf("%d",&pre[i]);
	}
	bintree bt;
	bt=create(n,pre,mid);
	jx(bt);
	pp(bt);
	return 0;
} 

下面看最后一道题:
给定一棵二叉树的先序遍历序列和中序遍历序列,要求计算该二叉树的高度。

输入格式:
输入首先给出正整数N(≤50),为树中结点总数。下面两行先后给出先序和中序遍历序列,均是长度为N的不包含重复英文字母(区别大小写)的字符串。

输出格式:
输出为一个整数,即该二叉树的高度。

输入样例:
9
ABDFGHIEC
FDHGIBEAC

输出样例:
5

直接附上代码,怎么做的思想已经在上文阐述过了,这里唯一一个需要注意的点就是求二叉树的高度,这也是一个递归函数,不难看懂源码。

#include <stdio.h>
#include <stdlib.h>
typedef struct node* bintree;
struct node{
	bintree l;
	bintree r;
	char data;
};

bintree createbintree(char a[],char b[],int n){
	bintree bt;
	int i;
	if(n==0){
		return NULL;
	}else{
		bt=(bintree)malloc(sizeof(struct node));
		bt->data=a[0];
		for(i=0;i<n;i++){
			if(b[i]==a[0])break;
		}
		bt->l=createbintree(a+1,b,i);
		bt->r=createbintree(a+i+1,b+i+1,n-i-1);
	}
	return bt;
}

int gettree(bintree bt){
	int hr,hl,h;
	if(bt==NULL){
		return 0;
	}else{
		hl=gettree(bt->l);
		hr=gettree(bt->r);
		if(hl<hr){
			hl=hr;
		}
		h=hl;
		h++;
	}
	return h;
}
int main(){
	int n,i;
	scanf("%d",&n);
	char f1[n],f2[n];
	scanf("%s",f1);
	scanf("%s",f2);
	bintree bt=createbintree(f1,f2,n);
	printf("%d",gettree(bt));
}
  • 8
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值