PTA Tree Traversals Again 思路分析及代码解析 v0.92

一、前导

1. 需要掌握的知识

  1. 二叉树及其非递归遍历
  2. 堆栈

2. 题目信息

  1. 题目来源:PTA / 拼题A
  2. 题目地址: Tree Traversals Again

二、解题思路分析

1. 题意理解

  1. 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 can be generated from this sequence of operations.
    Your task is to give the postorder traversal sequence of this tree.
  2. 输入数据
6		//树的结点数
Push 1  //向堆栈中push一个元素
Push 2
Push 3
Pop	//从栈顶弹出一个元素
Pop
Push 4
Pop
Pop
Push 5
Push 6
Pop
Pop
  1. 输出数据:打印树的后序遍历
3 4 2 6 5 1
  1. 题意
    堆栈的操作顺序,可唯一确定一棵二叉树,请输出此树的后序遍历

2. 思路分析(重点)

  1. 根据应用堆栈进行树的遍历的内容:堆栈的入栈顺序就是树的先序遍历,出栈顺序就是树的中序遍历
  2. 本题就是已知先序遍历和中序遍历,得到后序遍历

三、具体实现

1. 弯路和bug

  1. 由于使用递归,判定条件是inorder[InL+record],而不是inorder[record]
for(record=0;record<Nodes;record++)
		if(inorder[InL+record]==root)  //** bug: if(inorder[record]==root)
			break;

2. 代码框架(重点)

2.1 采用的数据结构

数组

#define max 30
int preorder[max], inorder[max],postorder[max];

2.2 程序主体框架

               程序伪码描述
int main()
{	
	题意:堆栈的push是先序遍历,pop是中序遍历,要获取后序遍历
	1.通过输入数据得到先序和中序
	2.通过先序和中序直接得到后续 
}

2.3 各分支函数

  1. GetPreAndIn( ) 通过输入数据,获取二叉树的先序遍历和中序遍历
#define size 10
char Operation[size]; 
void GetPreAndIn(int Nodes) //Nodes 树的结点数
{
	int data,top,pre=0,in=0;
	stack<int> s;
	for(int i=0;i<Nodes*2;i++)
	{
		cin>>Operation;
		if(!strcmp(Operation,"Push")) 
		{
			cin>>data;
			preorder[pre]=data; pre++; //入栈顺序就是树的先序遍历
			s.push(data);
		}
		else if(!strcmp(Operation,"Pop"))
		{
			top=s.top();
			s.pop();
			inorder[in]=top; in++; //出栈顺序就是树的中序遍历
		}
	} 
}
  1. GetPostOrder( ) 核心函数,根据先序和中序,不建树的情况下直接得到后序。具体思路是:
    (1)先序遍历的首元素就是root,root是后序的最后一个元素
    (2)已知root和中序遍历,可以得到二叉树左右子树的结点个数
    (3)递归左子树和右子树
    (4)需要考虑递归的两种特殊情况:树结点为空时和树结点为1时
void GetPostOrder(int PreL,int InL,int PostL,int Nodes) //PreL InL PostL 表示前序 中序 后序 最左边的元素编号,也就是0 
{
	int root,leftTreeNumber,rightTreeNumber, record=0;
	if(!Nodes) return;
	if(Nodes==1) 
	{
		postorder[PostL]=preorder[PreL];
		return;
	}
	root=preorder[PreL]; //对于先序,最左边的元素就是根元素 
	postorder[PostL+Nodes-1]=root;
	
	for(record=0;record<Nodes;record++)
		if(inorder[InL+record]==root)  //** bug: if(inorder[record]==root) 注意起始位置
			break;
	leftTreeNumber=record; rightTreeNumber=Nodes-leftTreeNumber-1;
	GetPostOrder(PreL+1,InL,PostL,leftTreeNumber); //左子树及其最左边的元素 
	GetPostOrder(PreL+record+1,InL+record+1,PostL+record,rightTreeNumber);
}

3. 完整编码

  1. 如果本文帮到了您,请点赞鼓励,您的鼓励是作者持续原创的动力,谢谢
  2. 如有疑问或建议,欢迎留言 😃
#include<stack>
#include<cstring>
#include<iostream>
using namespace std;

#define size 10
#define max 30
#define origin 0

int preorder[max], inorder[max],postorder[max];
char Operation[size]; 

void GetPreAndIn(int Nodes); 
void GetPostOrder(int PreL,int InL,int PostL,int Nodes);
void PrintOrder(int order[],int Nodes); 

int main()
{
	int Nodes; 
	cin>>Nodes;
	GetPreAndIn(Nodes);
	GetPostOrder(origin,origin,origin,Nodes);
	PrintOrder(postorder,Nodes);
	return 0;
}

void PrintOrder(int order[],int Nodes)
{
	cout<<order[origin];
	for(int i=1;i<Nodes;i++)
		cout<<' '<<order[i];
}

void GetPostOrder(int PreL,int InL,int PostL,int Nodes) //PreL InL PostL 表示前序 中序 后序 最左边的元素编号,也就是0 
{
	int root,leftTreeNumber,rightTreeNumber, record=0;
	if(!Nodes) return;
	if(Nodes==1) 
	{
		postorder[PostL]=preorder[PreL];
		return;
	}
	root=preorder[PreL]; //对于先序,最左边的元素就是根元素 
	postorder[PostL+Nodes-1]=root;
	
	for(record=0;record<Nodes;record++)
		if(inorder[InL+record]==root)  //** bug: if(inorder[record]==root) 注意起始位置
			break;
	leftTreeNumber=record; rightTreeNumber=Nodes-leftTreeNumber-1;
	GetPostOrder(PreL+1,InL,PostL,leftTreeNumber); //左子树及其最左边的元素 
	GetPostOrder(PreL+record+1,InL+record+1,PostL+record,rightTreeNumber);
}
void GetPreAndIn(int Nodes)
{
	int data,top,pre=0,in=0;
	stack<int> s;
	for(int i=0;i<Nodes*2;i++)
	{
		cin>>Operation;
		if(!strcmp(Operation,"Push")) 
		{
			cin>>data;
			preorder[pre]=data; pre++; 
			s.push(data);
		}
		else if(!strcmp(Operation,"Pop"))
		{
			top=s.top();
			s.pop();
			inorder[in]=top; in++;
		}
	} 
	
}

210930 AC代码:树的基础知识遗忘严重,完全忘记了 入栈顺序就是树的先序遍历,出栈顺序就是中序遍历。需要据此获得先序和中序,已知先序和中序就可以构建一棵树 或 直接推导出后序

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

void GetPost(int Pre, int In, int Post, int N);
int* PreOrder,*InOrder,*PostOrder;

int main()
{
	int N; cin >> N;	
	PreOrder = new int[N];	InOrder = new int[N]; 	PostOrder = new int[N];
	int PreCount = 0, InCount = 0;

	string Act;  int Value;
	stack<int> s;
	for(int i=0;i<2*N;i++)
	{
		cin >> Act;
		if (Act == "Push")
		{
			cin >> Value;
			s.push(Value);
			PreOrder[PreCount++] = Value; //先序遍历就是入栈的顺序
		}
		else
		{
			Value = s.top();
			s.pop();
			InOrder[InCount++] = Value; //中序遍历就是出栈的顺序
		}	
	}

	GetPost(0, 0, 0, N);

	cout << PostOrder[0];
	for (int i = 1; i < N; i++)
		cout<<" " << PostOrder[i] ;

	return 0;
}

//先序遍历、中序遍历、后序遍历的起始元素索引 , 当前树的结点数
void GetPost(int Pre, int In, int Post, int N) 
{
	if (!N) //子树为空时
		return;

	if (N == 1) //子树只有一个结点
	{
		PostOrder[Post] = PreOrder[Pre];
		return;
	}

	int CurrentRoot = PreOrder[Pre]; //先序遍历的首元素 = 根节点 = 后序遍历的最后一个元素
	PostOrder[Post + N - 1] = CurrentRoot;

	int Postion; //在中序遍历中找到根结点的位置 从而确定左右子树的个数
	for (Postion = In; Postion < In + N; Postion++) //注意中序遍历范围
		if (InOrder[Postion] == CurrentRoot)
			break;
	
	int LeftTree = Postion-In, RightTree = N - LeftTree -1; 

	GetPost(Pre + 1, In, Post, LeftTree);//递归左子树

	//递归右子树 注意起始点的计算。可以通过具体例子进行推导
	GetPost(Pre + LeftTree + 1, In+ LeftTree + 1, Post + LeftTree, RightTree);
}

四、参考资料

浙江大学 陈越、何钦铭老师主讲的数据结构

五、番外_根据先序和中序构建树

核心就是通过先序确定根元素;知道根元素,就可以通过中序确定左子树 和 右子树的个数,最后递归左右子树即可。
方法 和 通过前序和中序,推导后序是一样的

#include <iostream>
using namespace std;

#define null -1

typedef struct Node* BST;
struct Node
{
	int Value;
	BST Left;
	BST Right;
};

BST Construct(int PreStart, int InStart, int N);
void Print(BST Tree);

int* PreOrder, * InOrder;

int main()
{
	int N; cin >> N;

	PreOrder = new int[N];
	InOrder = new int[N];

	for (int i = 0; i < N; i++)
		cin >> PreOrder[i];
	for (int i = 0; i < N; i++)
		cin >> InOrder[i];

	BST Tree = Construct(0, 0, N); //前序起点 中序起点 树结点数

	Print(Tree);

	return 0;
}

BST Construct(int PreStart, int InStart, int N)
{
	if (N == 0)
		return NULL;

	if (N == 1)
	{
		BST Tree = new struct Node;
		Tree->Value = PreOrder[PreStart];
		Tree->Left = Tree->Right = NULL;
		return Tree;
	}

	int Root = PreOrder[PreStart], Left; //根元素值,左子树的个数
	BST Tree = new struct Node;
	Tree->Value = Root;
	Tree->Left = Tree->Right = NULL;

	int i = 0;
	for (; i < N; i++) //锁定根元素在中序遍历中的位置
		if (InOrder[i] == Root)
			break;

	Left = i - InStart; //获知左子树的个数了

	Tree->Left = Construct(PreStart + 1, InStart, Left); //分别递归左右子树
	Tree->Right = Construct(PreStart + 1 + Left, i + 1, N - Left - 1);

	return Tree;
}

void Print(BST Tree)
{
	if (!Tree)
	{
		cout << null << " ";
		return;
	}
	
	Print(Tree->Left);
	Print(Tree->Right);
	cout << Tree->Value << " "; //后序遍历

	return;
}

/*
intput
5
3 9 20 15 7  //preorder
9 3 15 20 7  //inorder

Tree Structure:

  3
9     20
   15    7


*/
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值