PTA Tree Traversals Again 思路分析及代码解析
一、前导
1. 需要掌握的知识
- 二叉树及其非递归遍历
- 堆栈
2. 题目信息
- 题目来源:PTA / 拼题A
- 题目地址: Tree Traversals Again
二、解题思路分析
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. - 输入数据
6 //树的结点数
Push 1 //向堆栈中push一个元素
Push 2
Push 3
Pop //从栈顶弹出一个元素
Pop
Push 4
Pop
Pop
Push 5
Push 6
Pop
Pop
- 输出数据:打印树的后序遍历
3 4 2 6 5 1
- 题意
堆栈的操作顺序,可唯一确定一棵二叉树,请输出此树的后序遍历
2. 思路分析(重点)
- 根据应用堆栈进行树的遍历的内容:堆栈的入栈顺序就是树的先序遍历,出栈顺序就是树的中序遍历
- 本题就是已知先序遍历和中序遍历,得到后序遍历
三、具体实现
1. 弯路和bug
- 由于使用递归,判定条件是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 各分支函数
- 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++; //出栈顺序就是树的中序遍历
}
}
}
- 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. 完整编码
- 如果本文帮到了您,请点赞鼓励,您的鼓励是作者持续原创的动力,谢谢
- 如有疑问或建议,欢迎留言 😃
#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
*/