03-树3 Tree Traversals Again (25 分)(前中序变后序)

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

谷歌翻译:
03-树3树遍历(25分)
可以使用堆栈以非递归方式实现顺序二进制树遍历。例如,假设当遍历6节点二叉树(编号为1到6的键)时,堆栈操作为: push(1); push(2); push(3); pop(); pop(); push(4); pop(); pop(); push(5); push(6); pop(); pop().然后可以从该操作序列生成唯一的二叉树(如图1所示)。你的任务是给出这棵树的后序遍历序列。

输入规格:
每个输入文件包含一个测试用例。对于每种情况,第一行包含正整数N(≤30),其是树中节点的总数(因此节点从1到N编号)。然后是2N行,每行描述一种格式的堆栈操作:“Push X”,其中X是被推入堆栈的节点的索引;或“Pop”表示从堆栈中弹出一个节点。

输出规格:
对于每个测试用例,在一行中打印相应树的后序遍历序列。保证存在解决方案。所有数字必须用一个空格分隔,并且在行的末尾不能有额外的空格。

样本输入:
6
Push 1
Push 2
Push 3
Pop
Pop
Push 4
Pop
Pop
Push 5
Push 6
Pop
Pop

样本输出:
3 4 2 6 5 1

看一下下面的简图便于理解:
在这里插入图片描述

题解:
前序:1 2 3 4 5 6
中序:3 2 4 1 6 5
通过 输入的 push 顺序,得到前序遍历的数组
通过 pushpop 顺序,得到 中序遍历的数组
通过前序和中序的数组,可以唯一确定一个后序遍历数组。

后序序列就是在每个子序列的末尾存储当前找出来的根节点,,然后进行递归直到结束

因为后序序列就是根节点在最后存储的。每次只存储当前的根节点,将根节点存储完毕,也就是结束了。

关键地方:就是在中序中找出根节点位置,以及左子序列的长度,右子序列的长度;以及当前后序序列中当前子序列的其实位置下标;还有前序序列中根节点的下标;

前序序列根节点的下标变化:1、PreL+1 2、PreL+L+1
中序序列中子序列的起始位置:1、InL 2、InL+L+1
后序序列中要存储的根节点的起始位置,也就是当前子序列的第一个位置:1、LastL 2、LastL+L
左右序列的长度:L和R

具体注释请看代码中的注释:

#include <iostream>
#include <cstdio>
#include <string>
#include <vector>
#include <queue>
#include <stack>
#include <algorithm>
using namespace std;
typedef long long ll;
#define maxn 1000005
#define mod 7654321

stack<int> s;
int pre[1000],mid[1000],last[1000];

//1 2 3 4 5 6
//3 2 4 1 6 5
//通过 输入的 push 顺序,得到前序遍历的数组
//通过 push 和 pop 得到 中序遍历的数组
//通过前序和中序的数组,可以唯一确定一个后序遍历数组。

//参数:PreL为当前前序序列的起始位置,即根节点位置
//InL为当前中序序列中的左或右子树的起始位置
//LastL为后序序列中子序列起始位置的下标
//n为当前左或右子树的长度

void Solve(int PreL,int InL,int LastL,int n)
{
    if(n==0)//若没有节点直接返回
        return;
    if(n==1)//若只有一个节点,直接将其赋值给后序第一个元素
    {
        last[LastL]=pre[PreL];
        return;
    }
    int Root=pre[PreL];//存储当前子序列的根节点的值
    //LastL+n-1 就是当前序列的最后一个位置,因为根节点一定在LastL为当前子序列起始位置,
	//n为当前序列总长度,-1为当前序列最后一个位置的下标
    last[LastL+n-1]=Root;//起始位置加总长-1就是最后一个位置
    int i;

    //在当前子序列长度n中找到根节点位置
    for(i=0;i<n;i++)
    {
        if(mid[InL+i]==Root)//找到的i的位置为当前子序列中的位置
            break;
    }
    int L=i,R=n-i-1;//当前左右子序列的长度

    //递归解决左右

    /*后一个前序序列的根节点(第一个参数PreL):
	1、当前节点的下一个节点(当前左子树的根)即PreL+1
	2、以及当前节点往后推左子树的长度L(当前右子树的根)即PreL+1+L
	//中序序列开始查找的位置(第二个参数InL)
	1、当前左子树的起点往后走即InL
	2、当前右子树的起点往后走左子树的长度L即InL+1+L
	//后序序列的存储位置,当前左子树的起点位置(第三个参数postL)
	1、即当前位置postL
	2、往后走L到右子树起点即postL+L
	//第四个参数(L,R)
	存储当前左右子树的长度,最后将其值都赋值给n
	*/
    Solve(PreL+1,InL,LastL,L);
    Solve(PreL+L+1,InL+L+1,LastL+L,R);
}

int main()
{
    int n,a,j=0,k=0;

    string str;
    cin>>n;
    for(int i=0;i<2*n;i++)
    {
        cin>>str;
        if(str=="Push")
            cin>>a,s.push(a),pre[j++]=a;
        if(str=="Pop")
            mid[k++]=s.top(),s.pop();
    }

    Solve(0,0,0,n);

    for(int i=0;i<n;i++)
    {
        if(i==0)
            cout<<last[i];
        else
            cout<<" "<<last[i];
    }
    cout<<endl;
    return 0;
}


  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值