1339:【例3-4】求后序遍历(树的经典题)

【题目描述】
输入一棵二叉树的先序和中序遍历序列,输出其后序遍历序列。

【输入】
共两行,第一行一个字符串,表示树的先序遍历,第二行一个字符串,表示树的中序遍历。树的结点一律用小写字母表示。

【输出】
一行,表示树的后序遍历序列。

【输入样例】
abdec
dbeac
【输出样例】
debca

这是一道已知先序和中序,求后序的程序。我将用递归的方法来讲解这一道题。刷到现在,发现做题是一件及其有意思的一件事。我希望把这种快乐传递给更多的人,哈哈哈哈哈。

1.了解先序、中序和后序遍历的访问顺序;
先序遍历:先访问根结点,再遍历左子树,最后遍历右子树,意味着在结点的先序序列中,第一个结点必定是根,假设为root(对于每颗子树也成立);
中序遍历:先遍历左子树,再访问根结点,最后遍历右子树,所有结点root正好把中序序列分成了两部分,root之前的应该是左子树上的结点,root之后的应该是右子树上的结点;
后序遍历:先遍历左子树,再遍历右子树,最后访问根结点,意味着在结点的后序遍历中,最后一个结点必定是根结点(对于每颗子树也成立)。
既然这样,我们只要递归将一棵大树分成两颗子树,然后找他们的父结点,不断递归输出;

2.根据中序遍历的访问顺序可以得知,它的根结点、左子树和右子树是非常好找的,比如,中序遍历的起点是l2,终点是r2;
定义pos为根结点的下标
那么,中序遍历的左子树起止范围:(l2, pos-1) ,右子树起止范围:(pos+1, r2)
就是根结点pos将其分为左右两边,左边就是左子树,右边就是右子树。

3.先序遍历的左子树和右子树不好找,难点在于找到先序遍历中的左子树和右子树。
睁大眼睛看好啦,哈哈哈哈
根据中序遍历的定义,得知
左子树长度:pos-L2;
右子树长度:R2-pos;

规定先序遍历的起点是L1,终点是R1;

先序遍历左子树的边界分析如下:
起点:L1+1(起点+1,因为起点是根结点,所以从下一个结点开始);
终点有两种计算方法:第一种:(起点+左子树长度-1) L1+1+(pos-L2)-1 = L1+pos-L2;
终点有两种计算方法:第二种:(终点-右子树长度) R1-(R2-pos);

先序遍历右子树的边界分析如下:
因为左子树的终点有两种方法,那么右子树就有两种写法。
第一种:
起点:(第一种方法起点+1)L1+pos-L2+1;
终点:(起点+右子树长度-1)L1+pos-L2+1+(R2-pos)-1 = L1-L2+R2;
第二种:
起点:(第二种方法起点+1)R1-(R2-pos)+1;
终点:(起点+右子树长度-1)R1-(R2-pos)+1+(R2-pos)-1 = R1;

确定好左右子树的边界以后,递归就很好写了,所以递归就有两种写法:
第一种:
if(pos > L2) dg(L1+1, L1+pos-L2, L2, pos-1);//递归左子树
if(pos < R2) dg(L1+pos-L2+1, L1-L2+R2, pos+1, R2);//递归右子树
第二种:
if(pos > L2) dg(L1+1, R1-(R2-pos), L2, pos-1);//递归左子树
if(pos < R2) dg(R1-(R2-pos)+1, R1, pos+1, R2);//递归右子树

#include<bits/stdc++.h>
using namespace std;
string before, between;
//左子树长度:pos-l2
//右子树长度:r2-pos
void dg(int l1, int r1, int l2, int r2){
	int pos = between.find(before[l1]);
	//方法一:
//	if(pos > l2) dg(l1+1, l1+pos-l2, l2, pos-1);//递归左子树 
//	if(pos < r2) dg(l1+pos-l2+1, l1-l2+r2, pos+1, r2);//递归右子树
	//方法二:
	if(pos > l2) dg(l1+1, r1-(r2-pos), l2, pos-1);//递归左子树 
	if(pos < r2) dg(r1-(r2-pos)+1, r1, pos+1, r2);//递归右子树 
	cout << between[pos];
}
int main(){
	cin >> before >> between;
	dg(0, before.size()-1, 0, between.size()-1);
	return 0;
}

小仙女~小帅哥看过来,是不是对求后序排列有了全面的了解,那就点个赞再走吧。

对于已知前序遍历和中序遍历结果,后序遍历结果的问,可以通过递归的方式来解决。具体步骤如下: 1. 首先,根据前序遍历的结果,确定根节点。前序遍历的第一个元素即为根节点。 2. 然后,在中序遍历的结果中找到根节点的位置,将中序遍历结果分为左子和右子。 3. 根据左子和右子的长度,将前序遍历结果分为左子和右子。 4. 递归地对左子和右子进行上述操作,得到左子和右子后序遍历结果。 5. 将左子后序遍历结果和右子后序遍历结果拼接在一起,并将根节点的值添加到最后,即得到整棵二叉树后序遍历结果。 下面是一个示代码实现: ```python def get_postorder(preorder, inorder): if not preorder or not inorder: return [] root = preorder[0] root_index = inorder.index(root) left_inorder = inorder[:root_index] right_inorder = inorder[root_index+1:] left_preorder = preorder[1:len(left_inorder)+1] right_preorder = preorder[len(left_inorder)+1:] left_postorder = get_postorder(left_preorder, left_inorder) right_postorder = get_postorder(right_preorder, right_inorder) return left_postorder + right_postorder + [root] preorder = [1, 2, 4, 5, 7, 8, 3, 6] inorder = [4, 2, 7, 5, 8, 1, 3, 6] postorder = get_postorder(preorder, inorder) print(postorder) # 输出 [4, 7, 8, 5, 2, 6, 3, 1] ``` 通过以上代码,可以得到给定前序遍历和中序遍历结果后,得的后序遍历结果为 `[4, 7, 8, 5, 2, 6, 3, 1]`。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值