1022: 二叉树
题目描述:
众所周知,遍历一棵二叉树就是按某条搜索路径巡访其中每个结点,使得每个结点均被访问一次,而且仅被访问一次。最常使用的有三种遍历的方式:
1.前序遍历:若二叉树为空,则空操作;否则先访问根结点,接着前序遍历左子树,最后再前序遍历右子树。
2.中序遍历:若二叉树为空,则空操作;否则先中序遍历左子树,接着访问根结点,最后再前中遍历右子树。
3.后序遍历:若二叉树为空,则空操作;否则先后序遍历左子树,接着后序遍历右子树,最后再访问根结点。
现在的问题是给定前序遍历和后序遍历的顺序,要求出总共有多少棵不同形态的二叉树满足这样的遍历顺序。
输入:
输入有多组数据,每组数据两行,第一行给出前序遍历的访问顺序,第二行给出后序遍历的访问顺序。
二叉树的结点用一个大写字母表示,不会有两个结点标上相同字母。输入数据不包含空格,且保证至少有一棵二叉树符合要求。
输出:
输出一个整数,为符合要求的不同形态二叉树的数目。
ABCD CBDA
2
。第一篇文章本来想做DP的,结果彼LIS的代码实现部分总还有些地方不如意;拖来拖去决定先把这篇有关二叉树的写了。
此题主要涉及到二叉树的三种循环方式,如果没有基础的新手或是间隔太久忘记了数据结构所讲的内容,最好先去看一看讲解二叉树遍历方式的帖子(比如:https://blog.csdn.net/soundwave_/article/details/53120766)。
那么我们稍微简单回顾一下:
本题实际上用到的两种遍历分别是前序遍历和后序遍历;
前序遍历:先遍历根节点(根 左 右),从效果上看是各分支从上到下从左到右、先记录根节点;
后序遍历:最后遍历根节点(左 右 根),从效果上看是各分支从下到上从左到右、最后记录根节点。
题目给定了(输入)前序遍历和后序遍历出的结果,要求求出总共有多少棵不同形态的二叉树满足这样的遍历顺序(输出)。
分析:
本题是一个并不困难的找规律问题,关键在于深入理解二叉树的遍历方法以及抽象出特例的能力。
首先我们来随便画一幅包含各种情况的二叉树:
(懒得画了,直接从讲解贴搬过来的)
此树的记录顺序为:
先序遍历:A BCD EFGHK
后序遍历:DCB HKGFE A
很显然地我们能看到先序遍历的左子树和后序遍历的左子树是相邻的,不同于右子树H、K返回G节点(显然H左、K右而G是父节点),单凭前后续遍历,我们无法判断A下的左子树和右子树前半部分下的节点是左节点还是右节点。
这即是问题的核心所在:
在只有前序遍历和后续遍历的情况下,我们无法判断某个单子树是左子树还是右子树。
而每次出现这种无法判断的情况都有“是左”和“是右”2种可能,故当判断时遇到第n次这种情况有2*2*2... 即 2^n种可能存在的二叉树。
那么现在我们的目标就非常明了了——找出单子树的个数。
由上图我们可以很清晰地看出单子树的规律——根(父)结点与“孩子”们前后遍历顺序相邻、记录顺序相反。
即: 【前序(孩子)==后续(孩子) && 前序首位(根节点)==后续末位(根节点)】
代码实现:
#include <bits/stdc++.h> using namespace std; int main() { char pre[2000],pos[2000]; while(cin>>pre>>pos){ int len=strlen(pre); int n=0; for(int i=0;i<len-1;i++){ for(int j=len-1;j>=1;j--){ if(pre[i]==pos[j]&&pre[i+1]==pos[j-1]) n++; } } long long ans=pow(2,n); cout<<ans<<endl; } return 0; }