03-树3 Tree Traversals Again(C语言) 一篇就让你弄懂

(网上找了一圈没有详细解答的,遂来补充)

先说思路:本题给出了一段由非递归实现的栈序列,由此按照此序列的操作可得到该树的中序遍历序列:3 2 4 1 6 5。但其实本题还包含了一个隐藏信息:我们回顾一下,先序遍历就是把第一次碰到的节点的值都打印出来。所以,本题中Push的节点的顺序,其实就是该树先序遍历的顺序,由此可得到先序遍历序列:1 2 3 4 5 6

(需要注意的一点,本题未说明节点的顺序一定是按照1 2 3 ...这样排列的)

所以,这道题的问题转化为:知道树的先序遍历序列和中序遍历序列,求树的后序遍历序列

实现这一功能的函数如下。该函数重点的思想是:遍历一棵树,用两个指针,一个指向先序序列中该树根节点的位置(pre),一个指向中序序列中该树节点的起始位置(in)。还有一个参数(num)表示该树共有多少个节点

(下图中Pre、In为存储先序、后序序列的数组)

void PostOrderTravelsal(int pre, int in, int num) {
	if (num == 0) return;             //子树为空
	if (num == 1) {                   //表明此子树为叶子节点,输出
		printf("%d", Pre[pre]);
        if (num != size) printf(" ");
		return;
	}
	int i, root, lnum, rnum, lroot, rroot, lin, rin;
	root = Pre[pre];
	for (i = 0;i < num;i++) {         //确定左子树节点数
		if (In[in+i] == root)
			break;
	}
	lnum = i, rnum = num - lnum - 1;  //lnum:左子树节点,rnum:右子树节点
	lroot = pre + 1;                  //左子树、右子树根节点在先序序列中的下标
	rroot = pre + lnum + 1;           
	lin = in;                         //左子树、右子树节点在中序序列中第一次出现的位置
	rin = (in + i) + 1;;
	PostOrderTravelsal(lroot, lin, lnum);
	PostOrderTravelsal(rroot, rin, rnum);
	printf("%d", root);
	if (num != size) printf(" ");
}

(这样说肯定有小伙伴不理解),下面我来解释一下什么意思

注:下图多写了个7

一开始调用PostOrderTravelsal(0,0,size),之后确定左子节点树,随即可确定左右子树在根节点的位置(lroot,rroot),之后确定lrin,rrin;这里:  

一个指向先序序列中该树根节点的位置(pre)”---pre指向树“123456”的根节点1;

一个指向中序序列中该树节点的起始位置(in)”--in指向树“123456”在中序序列中的第一个节点3

该树共有num个节点”--这里为6

后递归调用PostOrderTravelsal(1,0,3)到1的左子树“234”(注意此处num为3)

一个指向先序序列中该树根节点的位置(pre)”---pre指向树“234”的根节点2;

一个指向中序序列中该树节点的起始位置(in)”--in指向树“234”在中序序列中的第一个节点3

该树共有num个节点”--这里为3

之后的依次类推 

那之后便是lroot,rroot,lrin,rrin的推导:

for (i = 0;i < num;i++) {             //确定左子树节点数(节点数为i)
		if (In[in+i] == root)
			break;
	}
	lnum = i, rnum = num - lnum - 1;//lnum:左子树节点,rnum:右子树节点(总结点数-左节点数-1)
	lroot = pre + 1;                  //左子树、右子树根节点在先序序列中的下标
	rroot = pre + lnum + 1;           
	lin = in;                      //左子树、右子树节点在中序序列中第一次出现的位置
	rin = (in + i) + 1;;           //由于确定该树根节点下标为(in+i),则有:
                                   //lin:(in+i)- lnum = in
                                   //rin:(in+1) + 1 

 还有最后一点要注意,该递归函数什么时候跳出?一种情况,num=1,即找到叶子节点,打印;还有一种情况,num=0,即该树没有左子树(或右子树),这种情况不能忽略

 下面是全代码,复制即可运行

#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 30

void InputTree();
void PostOrderTravelsal(int pre, int in, int num);

int size;
int Pre[MAXSIZE] = { 0 }, In[MAXSIZE] = { 0 };   //定义为全局变量,方便操作

int main() {
	scanf("%d", &size);
	if (getchar());           //去除末尾换行符
	InputTree();
	PostOrderTravelsal(0, 0, size);
}


void InputTree() {  //有输入得到先序和中序序列
	int i, pre = 0, in = 0, top = -1, data;
	int stack[MAXSIZE] = { 0 };             //简易的堆栈实现
	char c1, c2;
	for (i = 0;i < 2 * size;i++) {
		scanf("%c%c", &c1, &c2);
		if (c2 == 'u') {
			scanf("%c%c %d", &c2, &c2, &data);
			Pre[pre++] = data;
			stack[++top] = data;
		}
		else {
			scanf("%c", &c1);
			In[in++] = stack[top--];
		}
		if (getchar());
	}
}
void PostOrderTravelsal(int pre, int in, int num) {
	if (num == 0) return;                   //字数为空
	if (num == 1) {
		printf("%d", Pre[pre]);
        if (num != size) printf(" ");
		return;
	}
	int i, root, lnum, rnum, lroot, rroot, lin, rin;
	root = Pre[pre];
	for (i = 0;i < num;i++) {
		if (In[in+i] == root)
			break;
	}
	lnum = i, rnum = num - lnum - 1;
	lroot = pre + 1;
	rroot = pre + lnum + 1;
	lin = in;
	rin = (in + i) + 1;;
	PostOrderTravelsal(lroot, lin, lnum);
	PostOrderTravelsal(rroot, rin, rnum);
	printf("%d", root);
	if (num != size) printf(" ");
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值