03-树3 Tree Traversals Again(根据先中序遍历序列构建二叉树)

目录

题干

解题思路

完整代码


题干:

An inorder binary tree traversal can be implemented in a non-recursive way with a stack. For example, suppose that when a 6-node binary tree (with the keys numbered from 1 to 6) is traversed, the stack operations are: push(1); push(2); push(3); pop(); pop(); push(4); pop(); pop(); push(5); push(6); pop(); pop(). Then a unique binary tree (shown in Figure 1) can be generated from this sequence of operations. Your task is to give the postorder traversal sequence of this tree.


Figure 1

Input Specification:

Each input file contains one test case. For each case, the first line contains a positive integer N (≤30) which is the total number of nodes in a tree (and hence the nodes are numbered from 1 to N). Then 2N lines follow, each describes a stack operation in the format: "Push X" where X is the index of the node being pushed onto the stack; or "Pop" meaning to pop one node from the stack.

Output Specification:

For each test case, print the postorder traversal sequence of the corresponding tree in one line. A solution is guaranteed to exist. All the numbers must be separated by exactly one space, and there must be no extra space at the end of the line.

Sample Input:

6
Push 1
Push 2
Push 3
Pop
Pop
Push 4
Pop
Pop
Push 5
Push 6
Pop
Pop

Sample Output:

3 4 2 6 5 1

解题思路:

        输入数据为一些列关于栈的操作,仔细观察会发现其出栈的序列是二叉树的中序遍历,入栈序列是二叉树的先序遍历,只需将两个序列提取出来,便可以很容易地构建一棵二叉树,然后对二叉树进行后续遍历即可得到二叉树的后序遍历序列。

        按照解题思路,一共分为三个步骤:

        1.建栈,构造先序序列和中序序列

        2.构造二叉树

        3.后序遍历二叉树,输出结果


1.建栈

        这里用的是C语言,所以需要自己造轮子,只需写初始化栈、入栈、出栈操作即可,比较简单:

typedef struct Stack Stack;
struct Stack
{
	int* stack;
	int top, size;
};
Stack* initStack(int size) {
	Stack* st = (Stack*)malloc(sizeof(Stack));
	st->stack = (int*)malloc(sizeof(int) * size);
	st->top = -1;
	st->size = size;
	return st;
}
int popElem(Stack* st) {
	if (st->top == -1)            //判空,但是这里可有可无
		return ERROR;
	return st->stack[st->top--];
}
int pushElem(Stack* st, int e) {
	if (st->top == st->size)        //判满,同样可有可无
		return ERROR;
	st->stack[++st->top] = e;
	return OK;
}

        然后在主函数中就可以构造先中序序列了:

char opt[5];
int num; scanf("%d", &num);
int* in = (int*)malloc(sizeof(int) * (num + 1)), * pre = (int*)malloc(sizeof(int) * (num + 1));
Stack* st = initStack(num);
TNode* root = NULL;

for (int i = 1, j = 1; i + j <= num * 2 + 1;) {
	scanf("%s", opt);
	if (opt[1] == 'u') {
		scanf("%d", &pre[i]);
		pushElem(st, pre[i]);
		i++;
	}
	else {
		in[j] = popElem(st);
		j++;
	}
}

改进:

        关于建栈操作其实可以简化,只需要一个数组模拟栈操作就行了:

char opt[5];
	int num, top = 1; scanf("%d", &num);
	int* in = (int*)malloc(sizeof(int) * (num + 1)), 
    * pre = (int*)malloc(sizeof(int) * (num + 1)), 
    * st = (int*)malloc(sizeof(int) * (num + 1));    //模拟栈
	TNode* root = NULL;

	for (int i = 1, j = 1; i + j <= num * 2 + 1;) {
		scanf("%s", opt);
		if (opt[1] == 'u') {
			scanf("%d", &pre[i]);
			st[top++] = pre[i];
			i++;
		}
		else {
			in[j] = st[--top];
			j++;
		}
	}

2.构造二叉树

        首先给出节点定义:

typedef struct TreeNode TNode;
struct TreeNode
{
	int data;
	TNode* left, * right;
};

        接下来也就是最核心的部分——根据先序和中序序列构造二叉树。虽然这一步看起来很难,但实际思路理解起来并不难。函数需要6个参数,前两个参数自然是先中序序列不说,后四个则是树的节点在两序列中的范围:

TNode* buildTree(int* in, int* pre, int preBegin, int preEnd, int inBegin, int inEnd);

        根据先序序列的特点我们可以看出来,对每一棵树(包括子树)来讲,先序遍历中的第一个节点就是它的根节点,所以我们只要确定每棵子树在先序序列中的范围,就能找到它的根节点,我们首先构造根节点,将根节点的数据存入,然后找出根节点在中序序列中的位置,根据中序序列的特点可知:根节点左右的节点就是该节点的左右子树,因而我们可以求出该根节点左右子树上的节点数量,由此可以确定子树节点在两序列中的范围。如果发现左右子树上的节点数量还大于0,我们就继续递归;如果为0,就说明该节点已经是叶节点,无需继续递归,直接将left和right设置为NULL即可。最后构造完成返回根节点:

TNode* buildTree(int* in, int* pre, int preBegin, int preEnd, int inBegin, int inEnd) {
	int leftLen, rigthLen, r;    //leftLen = 左子树节点个数 rightLen = 右子树节点个数 r = 根节点在中序序列中的位置
	TNode* root = (TNode*)malloc(sizeof(TNode));    //构造根节点
	root->data = pre[preBegin];                     //将根节点的数据存入根节点
	for (r = inBegin; in[r] != root->data; r++);    //寻找根节点在中序序列中的位置
	leftLen = r - inBegin;                          //计算左子树节点个数
	rigthLen = inEnd - r;                           //计算右子树节点个数
	if (leftLen)    //如果左子树不为空,继续构造子树
		root->left = buildTree(in, pre, preBegin + 1, preBegin + leftLen, inBegin, inBegin + leftLen - 1);
	else            //如果为空,则设置左节点为NULL
		root->left = NULL;
	if (rigthLen)    //如果右子树不为空,继续构造子树
		root->right = buildTree(in, pre, preEnd - rigthLen + 1, preEnd, inEnd - rigthLen + 1, inEnd);
	else            //如果为空,则设置右节点为NULL
		root->right = NULL;
	return root;    //返回根节点
}

3.后序遍历输出结果

        这一步没什么好说的,唯一值得注意的是末尾不能有多余空格,因而我采取了一些方法:

void postTraversal(TNode* root) {
	if (!root)
		return;
	static int flag = 1;
	postTraversal(root->left);
	postTraversal(root->right);
	if (flag) {
		printf("%d", root->data);
		flag = 0;
	}
	else
		printf(" %d", root->data);
}

完整代码:

#include <stdio.h>
#include <stdlib.h>

typedef struct TreeNode TNode;
struct TreeNode
{
	int data;
	TNode* left, * right;
};
TNode* buildTree(int* in, int* pre, int preBegin, int preEnd, int inBegin, int inEnd) {
	int leftLen, rigthLen, r;
	TNode* root = (TNode*)malloc(sizeof(TNode));
	root->data = pre[preBegin];
	for (r = inBegin; in[r] != root->data; r++);
	leftLen = r - inBegin;
	rigthLen = inEnd - r;
	if (leftLen)
		root->left = buildTree(in, pre, preBegin + 1, preBegin + leftLen, inBegin, inBegin + leftLen - 1);
	else
		root->left = NULL;
	if (rigthLen)
		root->right = buildTree(in, pre, preEnd - rigthLen + 1, preEnd, inEnd - rigthLen + 1, inEnd);
	else
		root->right = NULL;
	return root;
}
void postTraversal(TNode* root) {
	if (!root)
		return;
	static int flag = 1;
	postTraversal(root->left);
	postTraversal(root->right);
	if (flag) {
		printf("%d", root->data);
		flag = 0;
	}
	else
		printf(" %d", root->data);
}

int main()
{
	char opt[5];
	int num, top = 1; scanf("%d", &num);
	int* in = (int*)malloc(sizeof(int) * (num + 1)), 
	* pre = (int*)malloc(sizeof(int) * (num + 1)), 
	* st = (int*)malloc(sizeof(int) * (num + 1));
	TNode* root = NULL;

	for (int i = 1, j = 1; i + j <= num * 2 + 1;) {
		scanf("%s", opt);
		if (opt[1] == 'u') {
			scanf("%d", &pre[i]);
			st[top++] = pre[i];
			i++;
		}
		else {
			in[j] = st[--top];
			j++;
		}
	}
	root = buildTree(in, pre, 1, num, 1, num);
	postTraversal(root);

	return 0;
}

运行结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值