遍历二叉树(错题总结)

遍历二叉树2(题目来源:nkoj:3909)

时间限制 : - MS; 空间限制 : 65536 KB;评测说明 : 1s;

问题描述
给出两个由大写字母构成的字符串(长度不超过26),一个表示二叉树的前序遍历序列,一个表示二叉树的中序遍历序列,请你计算出该二叉树的后序遍历序列。

输入格式

两行,两个字符串分别表示前序和中序遍历的序列

输出格式

一行,一个字符串,表示后序遍历的序列

样例输入

ABGKLMCHJF
BLKMGAHCJF

样例输出

LMKGBHFJCA

一道基础的树类型的应用,简单来说就是知道前序,中序求后序,后面会补充知道后序,中序求前序。

思路:

因为前序的排列方式是:根,左,右;中序排列方式:左,根,右;将前序存放在string a中,中序存放在string b中,我们可以得知:root=a[0];此时中序就被分成了左右子树两个部分,接着再寻找左子树的root和右子树的root。因qi先从左子树找起,找完左子树找右子树。最后输出后序。

例:如果a=“ABC”,b="BAC",则root='A',此时b分成了‘BA’和‘AC’两个部分。因为A中B是A后的第一个字符,所以B是A的左子树。此时,b中只剩下了C。因C在A右边,所以C就是A的右子树。

那么解法就很明显了,通过前序和中序得到树的形状,再求出后序。

代码如下(恩····有误····但不知道错在哪里系列,希望有大佬能帮忙改改····)

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
char a[1005],b[1005];
int t,tree[28][4],yes,maxn;
bool s[28];
void print(int x)
{
	if (0<x&&x<=maxn)//后序输出 ; 
	{
		print(tree[x][2]);
		print(tree[x][3]);
		printf("%c",char(x-1+'A'));
	}
	return ;
}
bool pan(char x,char y)//寻找并判断是左树还是右树 ; 
{
	int ans=0;
	for (int i=0;i<t;i++)
	{
		if (b[i]==y)
		{
			if (i==0&&s[b[i+1]-'A'+1]==1||i==t-1&&s[b[i-1]-'A'+1]==1||s[b[i-1]-'A'+1]==1&&s[b[i+1]-'A'+1]==1)
			yes=1;//判断是否是底层叶子 ; 
			if(ans)
			return true;
			return false;
		}
		if(b[i]==x)
		ans=1;
	}
}
void check(int n,int m)
{
	int gen=0;
	for (int i=n+1;i<=m;i++)
	{
		int qt=a[i]-'A'+1;
		if (qt>maxn)
		maxn=qt;
		s[a[i]-'A'+1]=1;
		yes=0;
		if (pan(a[gen],a[i]))
		tree[a[gen]-'A'+1][3]=qt;
		else
		tree[a[gen]-'A'+1][2]=qt;
		tree[qt][1]=a[gen]-'A'+1;
		if (yes==0)
		gen=i;
	}
	return ;
}
int main()
{
//	freopen("ceshi.in","r",stdin);
	int i;
	cin>>a;
	scanf("\n");
	cin>>b;
	t=strlen(a);
	s[a[0]-'A'+1]=1;
	for (i=0;i<=t-1;i++)
	if (b[i]==a[0])
	{
		check(0,i);//建左子树 ; 
		break;
	}
	check(i,t-1);//建右子树; 
	print(a[0]-'A'+1);
	return 0;
}

 

不得不说这段代码特别长(确实emmm),还是错的(是我自己的问题吧····)所以有没有更简单的方法呢?答案就是省略掉中间建树的过程,直接输出后序,这里我们运用到了递归(递归蒟蒻不想说话····)。先放代码再进行解释(恩,能AC)

#include <bits/stdc++.h> 
using namespace std;  

const int maxn = 10000 + 5;
char s1[maxn], s2[maxn];
int a, b, ans;
void build(int n, char *str1, char *str2) {
	if(n <= 0) return;
	int s = strchr(str2, str1[0]) - str2;
	build(s, str1 + 1, str2);
	build(n - 1 - s, str1 + s + 1, s + str2 + 1);
	cout << str1[0];
}
int main() {
	cin >> s1 >> s2;
	int len = strlen(s1);
	build(len, s1, s2);
	cout << endl;
	return 0;
}

解析:

用s1存前序,s2存中序,因为s1和s2的长度是一模一样的,所以我们只需要测量s1的长度即可。主函数大概就分为:输入——测长——输出即可,关键部分在于build函数。

首先先看参数部分,n代表的是当前的根节点(root),*str1是一个指针函数,str1中特定长度的字符,*str2同理(不太清楚的朋友,可以先去了解一下指针)。

第一步:当n==0时返回,此时代表已经找到了左儿子或者右儿子。

当不满足第一步,执行第二步:在str2中查找str1的第一个字符的位置,并存在s中(注:返回的是一个地址,减去str2代表返回字符所在的位置)。然后,递归函数——>寻找左子树——>寻找游子树。//这一步过程实际上就是在隐形建树,但不保存。

第三步:输str1当前的第一个字符。//后面三项操作,实际上就是正常对树输出后序的操作,如果要输出中序,或者前序,改动str1的位置就能实现。(或者也可以说,因为实在两个递归函数之后,所以是输出后序emmmm...)

文字说明可能不是很清晰,接下来我们就距离说明一下(不会插入图片的我大概只能用文字了qwq,见谅):

int main:已知前序:ABDEC,中序:DBEAC,测得长度len=5;开始进行函数;

build(1(root)):(前提:n=5,str1=“ABDEC”,str2=“DBEAC”)因n>0,查找str1[0](A),得到s=3;进入递归1;

build(1(left)):(前提:n=3,str1="BDEC",str2="DBEAC")因为n>0,查找str1[0](B),得到s=1;进入递归1;

重复上述操作,直到n=0时(也就是查找到D时)

build(2(left)):(前提:n=0,str1='EC',str2='DBEAC')因n=0,return;返回到:(n=1,str1=‘DEC’,str2='DBEAC')的位置,进入递归2;

build(2(right)):(前提:n=1-0-1=0,str1='DEC'+0+1='EC',str2='DBEAC'+0+1='BEAC')因n=0,return;返回(n=1,str1=‘DEC’,str2=‘DBEAC’),输出。

重复上述操作(大概过程就是这样啦)

总结:

每次写代码都是一种我的代码是如此的优美,然后本地AC提交WA,好不容易做出一道题被别人说是水题qwq。自己的递归这一块还是比较欠缺,看来还是需要多刷题啊emmmmm。但也有代码功底不强的情况,平常写的博客太少了所以经验欠缺(是这样吗????)。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值