03-树3 Tree Traversals Again (25分)
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
题意
给定二叉树各结点入栈出栈的规则,让我们求二叉树的后序遍历。
由给定顺序可知,push的顺序为二叉树的前序遍历,
pop的顺序为二叉树的中序遍历,故模拟堆栈的进程,即可将二叉树的前序与中序遍历填满;
由此题目转化成由二叉树的前序和中序遍历求二叉树的后序遍历;
解题1:由前序遍历和中序遍历构建完整的二叉树,然后再逐个输出二叉树的后序遍历;
同样用结构数组存储二叉树即可。
建树:
设置标记位flag,
连续的push则放在上一个结点的left;
pop之后的push则放在上一个结点的right;
建树之后递归输出后序遍历即可,左右根。
#include<cstdio>
#include<stack>
#include<string>
#include<iostream>
const int maxn=35;
using namespace std;
struct TNode{
int left, right;
}T[maxn];
int count=0, n;
void print(int root){
if(root==-1)return ;
if(T[root].left!=-1)print(T[root].left);
if(T[root].right!=-1)print(T[root].right);
count++;
printf("%d", root);
if(count<n/2)printf(" ");
}
int main(){
int num, cnt=0, temp, root, flag=0, first;
scanf("%d\n", &n);
for(int i=1; i<=n; i++){//注意题目说角标是从1开始的
T[i].left=-1;
T[i].right=-1;
}
n=n*2;
string s;
stack<int> st;
for(int i=0; i<n; i++){
cin>>s;
if(s=="Push"){
cin>>num;
st.push(num);
if(i==0){
root=num;
first=num;
}else{
if(flag==0)T[root].left=num;
else T[root].right=num;
root=num;
flag=0;
}
}else{
root=st.top();
st.pop();
flag=1;
}
}
print(first);
return 0;
}
解题2:递归方式处理前序中序后序遍历,由前两者推后者
递归函数:
确定前中后序的起始坐标PRE,MID,POST,要处理的节点个数n,
递归三要素:
1.终止条件:n == 0时,无结点需要处理;n == 1时,将该结点放入后序遍历给出的位置
2.缩小规模:将前序遍历的第一个结点,即头节点,放置再后序遍历的最后一个位置,即POST+n-1处
3.调用自身:分别对中序遍历中头节点左右两段进行递归调用,利用树的递归特性
注意点
查找中序遍历左右个数时,要从MID下标开始查找,才是正确个数;
void solution(int PRE,int MID, int POST, int n){
if(n==0) return; //没有结点要处理
if(n==1) {
Post[POST]=Pre[PRE];
return;
}
int L,R;
//前序的第一个,放在后序的最后一个,
Post[POST+n-1]=Pre[PRE];
//放完之后,左右继续solusion
//通过中序计算左右剩余的结点个数
for(int i=0;i<N;i++){
if(Mid[MID+i]==Pre[PRE]){ //这里很关键——Mid[MID+i],表示该头的第i个
L=i; //找到左边坐标,个数为l+1
break;
}
}
R = n-L-1; //减掉刚才的根节点
solution(PRE+1,MID,POST,L);
solution(PRE+L+1,MID+L+1,POST+L,R);
}
完整代码:
#include<iostream>
#include<cstring>
#include<stack>
using namespace std;
int N;
int Pre[35];
int Mid[35];
int Post[35];
int temp;
void solution(int PRE,int MID, int POST, int n){
if(n==0) return; //没有结点要处理
if(n==1) {
Post[POST]=Pre[PRE];
return;
}
int L,R;
//前序的第一个,放在后序的最后一个,
Post[POST+n-1]=Pre[PRE];
//放完之后,左右继续solusion
//通过中序计算左右剩余的结点个数
for(int i=0;i<N;i++){
if(Mid[MID+i]==Pre[PRE]){ //这里很关键——Mid[MID+i],表示该头的第i个
L=i; //找到左边坐标,个数为l+1
break;
}
}
R = n-L-1; //减掉刚才的根节点
solution(PRE+1,MID,POST,L);
solution(PRE+L+1,MID+L+1,POST+L,R);
}
int main()
{ int num;
string cmd;
stack <int> st;
int m=0,n=0;
cin>>N;
for(int i=0;i<2*N;i++)
{
cin>>cmd;
if(cmd=="Push"){
cin>>num;
st.push(num);
Pre[m++]=num;
}
else{
temp=st.top();
st.pop();
Mid[n++]=temp;
}
}
//这时,已得到前序和中序遍历。
solution(0,0,0,N);
for(int i=0;i<N;i++){
if(i==N-1){
cout<<Post[i];
}
else cout<<Post[i]<<" ";
}
}