18724 二叉树的遍历运算—— SCAU 数据结构——根据先序和中序,确定后序

利用先序和中序构造二叉树并求后序遍历
这篇博客探讨如何根据先序和中序遍历序列构建二叉树,并导出后序遍历序列。通过分治思想,使用递归方法拆分和合并二叉树节点,最终实现从给定序列构造唯一二叉树并输出后序遍历顺序。示例展示了具体的操作过程和C语言实现。

C,核心代码有详细注释,自写,请读者视情参考

思想:

根据中序排序的特点我们可以知道,如果我们选中其中某一节点,它的左边一定是这个节点的左子树,右边一定是这个节点的右子树。
根据先序排序的特点我们可以知道,如果我们选中其中某一节点,它右边的节点一定是先排列完左子树,再排列右子树。

那么我们能否根据这两个规律,确定唯一的二叉树?

如果确定能够构造唯一的二叉树,那么根据后序遍历的算法很容易得出后序序列。

所需知识:

C,二叉树,先序遍历,中序遍历,后序遍历

途径:

采用分治思想,先分后合。将左子树和右子树逐层分开,直到左边没有节点,右边没有节点,此时该节点为叶子节点,再进行合并,接上上一层的节点。分治法一般采用递归实现拆分和合并,所以这里用一个递归函数来实现。

举例:

(建议直接看代码,不清楚再看这里)

对于示例先序pre[] = abcde,中序in[] = bcade,从先序第一个节点a开始,在中序中找到a,则a的左子树为preL[] = bc(这里可以看出左子树是两个节点),右子树为preR[] = de(两个)。

那么,我们可以知道先序中a的后面两个节点是左子树inL[] = bc,再后面两个是右子树inR[] = de。把左子树接上a,右子树接上a(因为是递归,所以会从底层一个一个接上的)。

对左子树再进行拆分,先序preL[] = bc,中序inL[] = bc,从先序第一个节点b开始,在中序中找到b,则b的左子树为 (这里可以看出该节点没有左子树),右子树为c(一个)。

那么,我们可以知道先序中b的后面零个节点是左子树,再后面一个是右子树。再对右子树进行拆分,发现右子树只有一个,已经是叶子了,那么接上上一层。

右子树同样道理,拆分到底层叶子不可再拆分,再从底层一层一层接上去。

.

当然,若是对后序比较熟悉的话,就会发现,这接上去的顺序其实就是后序的顺序,针对这道题目可以直接输出。

所以这道题有更简单的写法,简单写法代码贴在最后以供参考,但这道题目的是理解三种序列的特点,熟练的读者可以尝试自行修改。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#pragma warning(disable:4996)
#define ElemType char
#define OK 1
#define  ERROR 0
#define MaxNodeCount 50
typedef int Status;

typedef struct BiTreeNode
{
	ElemType data;
	struct BiTreeNode *lchild;
	struct BiTreeNode *rchild;
}BiTreeNode, *Bitree;

Bitree Stack[MaxNodeCount];//栈
int base = 0, top = 0;

char pre[100];//先序
char in[100];//后序

//根据给出前序和中序序列,创建二叉树。
//preL:前序左界,preR前序右界,inL中序左界,inR中序右界
//返回:当前构造子树根节点
Bitree PreInCreat(int preL, int preR, int inL, int inR) {
	Bitree root;
	root = (BiTreeNode *)malloc(sizeof(BiTreeNode));//建立子树的根节点
	root->data = pre[preL];//节点对应的数值
	int i;
	for (i = inL; in[i] != root->data; i++);//在中序中找到这个数值的位置
	int lLen = i - inL;//计算左子树的长度
	int rLen = inR - i;//计算右子树的长度
	if (lLen)//若左树非空,再构造左子树
	{
		root->lchild = PreInCreat(preL + 1, preL + lLen, inL, inL + lLen - 1);
		//请自行画数轴确定要传入的左界和右界
	}
	else root->lchild = NULL;//若左树为空,接上空节点
	if (rLen)
	{
		root->rchild = PreInCreat(preR - rLen + 1, preR, inR - rLen + 1, inR);
	}
	else root->rchild = NULL;

	return root;
}

//后序非递归遍历,详情请看《二叉树后序非递归遍历》
Status PostOrder(Bitree &T, Status(*Visit)(Bitree &)) {
	if (T == NULL)
	{
		return ERROR;
	}
	Bitree p = T, r = NULL;
	base = 0; top = 0;
	while (p || base != top) {
		if (p) {
			Stack[top++] = p;
			p = p->lchild;
		}
		else {
			p = Stack[top - 1];
			if (p->rchild&&r != p->rchild) {
				p = p->rchild;
			}
			else {
				p = Stack[--top];
				if (!Visit(p))return OK;
				r = p;
				p = NULL;
			}
		}
	}
}

Status PrintData(Bitree &T) {
	printf("%c", T->data);
	return OK;
}

Status DoNothing(Bitree &T) {
	return OK;
}

Status main() {
	scanf("%s", pre);
	scanf("%s", in);
	Bitree T = PreInCreat(0,  strlen(pre)-1, 0, strlen(in)-1);//注意输入的左右界
	PostOrder(T, PrintData);
}

Description
二叉树的三种遍历都可以通过递归实现。
如果我们知道一棵二叉树的先序和中序序列,可以用递归的方法求后序遍历序列。

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

输出格式
一个字符串,树的后序序列。

输入样例
abcde
bcade

输出样例
cbeda

c版本:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#pragma warning(disable:4996)
#define ElemType char
#define OK 1
#define  ERROR 0
#define MaxNodeCount 50
typedef int Status;

char pre[100];//前序
char in[100];//中序

void PreInCreat(int preL, int preR, int inL, int inR) {
	char data = pre[preL];
	int i;
	for (i = inL; in[i] != data; i++);//在中序中找到对应的前序位置
	int lLen = i - inL;//左子树的大小
	int rLen = inR - i;//右子树的大小
	if (lLen)//如果有左子树,则继续划分
		PreInCreat(preL + 1, preL + lLen, inL, i - 1);
	if (rLen)//如果有右子树,则继续划分
		PreInCreat(preR - rLen + 1, preR, i + 1, inR);
	printf("%c", data);//如果是叶子,则输出
}


Status main() {
	scanf("%s", &pre);
	scanf("%s", &in);
	PreInCreat(0, strlen(pre) - 1, 0, strlen(in) - 1);
}

c++版本

#include <iostream>
#include <cstring>
using namespace std;

char pre[128];
char in[128];

void PrintByPost(int inl, int inr, int prel, int prer) {
	char c = pre[prel];
	int i;
	for (i = 0; in[i] != c; i++);
	if (i - inl)PrintByPost(inl, i - 1, prel + 1, prel + i - inl);
	if (inr - i)PrintByPost(i + 1, inr, prel + i - inl + 1, prer);
	cout << c;
}

int main() {
	cin >> pre >> in;
	PrintByPost(0, strlen(in) - 1, 0, strlen(pre) - 1);
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值