原题目: 1138 Postorder Traversal (25 分)
题意
给出前序和中序序列,
👉要求输出后序遍历序列的第一个元素。
分析 V1
通过前序和中序序列生成后序序列。
- 关键:利用前序序列第一个元素总是根结点的特点,找到根结点在中序序列中的位置,将前序和中序序列划分为左子树区间和右子树区间,再往下递归。
- 递归式:生成后序序列,则逻辑同后序遍历;“访问根结点”操作——压入post数组中。
- 递归边界:当划分区间不成立,即区间小于0时【注意:不能取等号】。
CODE V1
#include <iostream>
#include <vector>
using namespace std;
int n;
vector<int> pre, in, post;
void createPost(int prel, int prer, int inl, int inr);
int main()
{
cin >> n;
pre.resize(n);
in.resize(n);
for ( int i=0; i<n; i++ ) cin >> pre[i];
for ( int i=0; i<n; i++ ) cin >> in[i];
createPost(0, n-1, 0, n-1);
cout << post[0];
return 0;
}
void createPost(int prel, int prer, int inl, int inr){
if ( prel>prer ) return; //递归边界:先序序列的区间小于0
//pre[prel]为根结点,找到其在中序序列中的位置(下标)
int k = inl; //当前根结点在中序序列中的位置
while( pre[prel]!=in[k] ) k++; //循环结束后,找到位置
//后序遍历(递归式)
createPost(prel+1, prel+k-inl, inl, k-1); //往左子树递归
createPost(prel+k-inl+1, prer, k+1, inr); //往右子树递归
post.push_back(in[k]); //访问根结点:将后序序列放入post数组中
}
分析 V2
题目只要求输出后序序列的第一个元素,故其实并不需要生成完整的后序序列——考虑 提前退出递归 。
- 单独设置一个标记flag——用于① 控制输出(否则会在回退的路上胡乱输出),② 提前退出递归。
- 递归边界:改用中序区间——在V1中,createPost函数的参数prer其实用处不大,可以省去,并且改用中序序列的区间作为递归边界。
CODE V2
#include <iostream>
#include <vector>
using namespace std;
int n;
vector<int> pre, in;
bool flag = false; //标记是否输出第一个后序序列元素,默认为未输出false
void Post(int prel, int inl, int inr);
int main()
{
cin >> n;
pre.resize(n);
in.resize(n);
for ( int i=0; i<n; i++ ) cin >> pre[i];
for ( int i=0; i<n; i++ ) cin >> in[i];
Post(0, 0, n-1);
return 0;
}
void Post(int prel, int inl, int inr){
if ( inl>inr || flag ) return; //递归边界:①先序序列的区间小于0,②已经按题意输出
//pre[prel]为根结点,找到其在中序序列中的位置(下标)
int k = inl; //当前根结点在中序序列中的位置
while( pre[prel]!=in[k] ) k++; //循环结束后,找到位置
//后序遍历(递归式)
Post(prel+1, inl, k-1); //往左子树递归
Post(prel+k-inl+1, k+1, inr); //往右子树递归
if ( !flag ){ //有条件地访问根结点
cout << in[k];
flag = true; //标记为已输出
}
}
知识点
- “前序+中序 转 后序”的逻辑——关键在于找根结点的中序位置后分左右子树区间。👉 传送门:&3 给定先序和中序遍历序列,重建二叉树⭐⭐【重要】
- 回溯法——在到达递归边界前由于某些事实可以直接返回上一层(感觉其实也是增加了一条递归边界啦(~ ̄▽ ̄)~)。👉初次间面传送门:n皇后问题的算法改进
词汇
CHUNK | 释义 |
---|---|
corresponding binary tree | 对应的,相应的 |