【PAT甲级】1119 Pre- and Post-order Traversals(前序后序转中序)
【题意】
根据二叉树的前序和后序序列,如果中序序列唯一,输出Yes,如果不唯一输出No,并输出这个中序序列。
【题解】
众所周知,二叉树是不能够根据前序和中序建立的,为什么呢?首先需要明确先序序列的遍历顺序是:根左右,后序序列的遍历顺序是:左右根。
然后我们来说一下这个样例(为了更好的说明不唯一性,没有用题目给出的样例):
前序:1 4 6 3 2 5 7
后序:3 6 4 5 7 2 1
首先1肯定是整棵二叉树的根节点,然后根据前序序列我们可以知道4是1的子树(此时还不确定是左子树还是右子树),然后可以得到在后序序列中4的位置,由于后序序列的遍历顺序是左右根,那么很容易可以确定3和6肯定是4的子节点,这个时候我们可以知道已4为根节点的子节点有2个(代码中也就是num),然后到前序序列去算,发现prer-prel - 1(也就是去掉1 4 )发现大于num,由于前序遍历序列是根左右,所以prer - prel -1 - num > 0不就说明去掉左子树之外还存在节点嘛,那我们刚才的以4为根节点的树就是1的左子树,2 5 7即是右子树。
然后我们继续递归(也就是说把刚才的序列分成左区间(前序 4 6 3 后序 3 6 4)和右区间(前序2 5 7 后序5 7 2)),然后我们来看左子树吧,这时候可以知道6是4的子树,到后序序列里找到6,那么可以确定3是6的子节点,这时我们可以知道以6位根节点的子节点有1个,然后发现前序序列中也只有1个3,这时候我们就无法确定6是4的左子树还是右子树了。
【代码】
#include<bits/stdc++.h> using namespace std; const int maxn = 35; int pre[maxn], post[maxn]; int f = 1; vector<int>v; void getorder(int prel, int prer, int postl, int postr) { if (prel == prer) { v.push_back(pre[prel]); return; } int a = pre[prel + 1]; int postidx = postl; while(post[postidx] != a && postidx <= postr)postidx++; int num = postidx - postl;//以a为根节点的子树的节点数 if (prer - prel - 1 == num)f = 0; /*在前序序列中,如果以pre[prel]为根节点的子树的节点数-a这个节点等于num,4 那么久无法判断a是在pre[prel]的左子树还是右子树,这里我们默认为左子树*/ getorder(prel + 1, prel + num + 1, postl, postidx); v.push_back(pre[prel]); /*在前序序列中,如果以pre[prel]为根节点的子树的节点数-a这个节点大于num, 因为前序的遍历序列是根左右,那么就说明以pre[prel]为根的树还有右子树*/ if (prer - prel - 1> num) getorder(prel + num + 2, prer, postidx + 1, postr - 1); } int main() { int n; scanf("%d", &n); for (int i = 0; i < n; i++) scanf("%d", &pre[i]); for (int i = 0; i < n; i++) scanf("%d", &post[i]); getorder(0, n - 1, 0, n - 1); if(f)printf("Yes\n"); else printf("No\n"); printf("%d", v[0]); for (int i = 1; i < n; i++) printf(" %d", v[i]); printf("\n"); }