1 基本数组建树
①求树高(后序)
整体看是求左树高和右树高中高一点的那颗树 +1
本质上就是:左、右、根
关键点:我到了一个空点,要返回给我的父节点什么信息
第一种:如果是0,就返回父亲说我的高度为0,因为自己没有高度
第二种:非0,那么我得统计完我自己的左和右高度才能告诉父亲的高度
#include<iostream>
using namespace std;
struct tree{
int data;
int l,r;
};
tree t[110];
int dfs(int i){
if(t[i].data==0)return 0;
int h1 = dfs(t[i].l);
int h2 = dfs(t[i].r);
return max(h1,h2)+1;
}
int main(){
int n;
cin>>n;
int d,l,r;
for(int i=1;i<=n;i++){
cin>>d>>l>>r;
t[i].data = d;
t[i].l = l;
t[i].r = r;
}
int height = dfs(1);
cout<<height;
return 0;
};
②竞赛题 FBI树(后序数组建树、后序数组输出)
每组输入数据的第一行是一个整数N(0<=N<=10),第二行是一个长度为2N的“01”串。
数据规模:
对于40%的数据,N<=2;
对于全部的数据,N<=10。
输出格式
每组输出包括一行,这一行只包含一个字符串,即FBI树的后序遍历序列。
输入样例
3
10001011
输出样例
IBFBBBFIBFIIIFF
注意:size of array 'c' is not an integral constant-expression
char c【N】不能用const int N = ...,来规定长度,数组 'c' 的大小不是整型常量表达式
树的一些性质
1、一颗满二叉树的高度为n,一共有2^n - 1个结点
2、每一层的最左下标就是这一行的元素个数,每一层的最后一个下标就是(最左下标*2-1)
#include<iostream>
using namespace std;
char c[2058];
string s;
int L;
void TreeBuild(int root){
//倒数第二层开始
if(root>=L)return;
TreeBuild(root*2);
TreeBuild(root*2+1);
//左右子树键好后父亲开始
if(c[root*2]=='B'&&c[root*2+1]=='B')c[root] = 'B';
else if(c[root*2]=='I'&&c[root*2+1]=='I')c[root] = 'I';
else c[root] = 'F';
}
//后序遍历
void dfs(int root){
//最后一层开始
if(root>=2*L)return;
dfs(root*2);
dfs(root*2+1);
cout<<c[root];
}
int main(){
int n;
cin>>n;//2^n长的字符串,然而并没有什么用,这个值
cin>>s;
L = s.length();
//将字符存储在 len - 2*len - 1当中
for(int i=0,j=L;i<L;i++,j++){
if(s[i]=='1')c[j] = 'I';
else c[j] = 'B';
}
//开始建树
TreeBuild(1);
dfs(1);
return 0;
}
2 链表建树
单独只告诉先序、中序、后序是无法建树的,像本道题由于有#的出现才能确定一颗树
那么可以确定一颗树就是知道:
知道一个中序:左 根 右 + 一个先序或者后序确定根即可
①含‘#’的先序建树、中序输出
1、typedef {}*node;不是node*
2、malloc的c++写法 Node node = new TreeNode()
3、建树的过程中统计叶子节点个数
4、先序建树思路:根、 左 、右 关键点:对于每个根本质上是结点,那么建不建这个结点依据是字符是否为‘#’,树的根肯定是不为‘#’所以不用考虑。
关键点:那么我 根 建完后要返回父亲什么信息呢?
第一种:如果为‘#’直接返回NULL给父亲
第二种:如果非‘#’,我需要继续建好我的子树才能给父亲,所以 left 继续create,right继续create
//利用flag剪枝
//中序遍历:左到根开始回退 中 右到根回退中的上一层
#include<iostream>
using namespace std;
typedef struct TreeNode{
char op;
struct TreeNode *left;
struct TreeNode *right;
}*Node;
string s;
int i,cnt;
Node InOrder(){
char c = s[i++];
if(c=='#'){
return NULL;
}
Node root = new TreeNode();
root->op = c;
root->left = NULL;
root->right = NULL;
root->left = InOrder();
root->right = InOrder();
if(root->left==NULL&&root->right==NULL){
cnt++;
}
return root;
}
void Midprint(Node head){
if(head->left!=NULL)Midprint(head->left);
printf("%c",head->op);
if(head->right!=NULL)Midprint(head->right);
}
int main(){
cin>>s;
//Inorder先序建树的同时统计叶子结点
Node root = InOrder();
Midprint(root);
cout<<'\n'<<cnt<<'\n';
return 0;
}
②知道中序和后序建树,先序输出
BADC
BDCA
ABCD
#include<iostream>
using namespace std;
string s1,s2;
void sol(int l1,int r1,int l2,int r2){
if(l1>r1||l2>r2)return;
int pos;
char op = s2[r2];
for(int i=l1;i<=r1;i++){
if(s1[i]==op){
cout<<op;
pos = i;
break;
}
}
sol(l1,pos-1,l2,l2+pos-l1-1);
sol(pos+1,r1,l2+pos-l1,r2-1);
}
int main(){
cin>>s1>>s2;
sol(0,s1.length()-1,0,s2.length()-1);
return 0;
}
1 dfs(中序左右,后序左右)
2 在 str1的(l1,r1)中 pos = FInd ( s2 [r2] )
3 先序输出可以cout<< str1[pos],break
4 dfs 左子树的中序左右:( l1, pos-1) ,后序左右(l2 , l2 + pos - l1 - 1)
5 dfs 右子树中序左右(pos + 1,r1),后序左右(l2+pos-l1,r2-1);
6 递归出口条件记住就行
注:传入后序左右的目的是为了确认“根”
③ 知道中序和先序,后序输出
ABEDFCHG
CBADEFGH
注意s从0开始就行,其他和②一样
#include<iostream>
using namespace std;
string s1,s2;
//ABEDFCHG 中
//CBADEFGH 先 根 左 右
void sol(int l1,int r1,int l2,int r2){
if(l1>r1||l2>r2)return;
//在先序中找到根
int pos;
char op = s2[l2];
//在中序中找到根
for(int i=l1;i<=r1;i++){
if(s1[i]==op){
pos = i;
break;
}
}
//传入左子树在先序中的左右
sol(l1,pos-1,l2+1,l2+(pos - l1));
//传入右子树在先序中的左右
sol(pos+1,r1,l2+(pos-l1)+1,r2);
//根输出
cout<<s1[pos];
}
int main(){
cin>>s1>>s2;
//传入中序和先序的左右
sol(0,s1.length()-1,0,s2.length()-1);
return 0;
}
④知道中序和按层序列,先序输出
#include<iostream>
using namespace std;
string s1,s2;
void sol(int l1,int r1,int l2,int r2){
if(l1>r1)return;
int pos;
int flag = 0;
//遍历层序在中序中找到根
for(int i=l2;i<=r2;i++){
for(int j=l1;j<=r1;j++){
if(s1[j]==s2[i]){
pos = j;
cout<<s1[j];
flag = 1;
break;
}
}
if(flag==1)break;
}
//传入左子树在先序中的左右
sol(l1,pos-1,l2,r2);
sol(pos+1,r1,l2,r2);
}
int main(){
cin>>s1>>s2;
sol(0,s1.length()-1,0,s2.length()-1);
return 0;
}
遍历层序所有点 for i l2 - r2
嵌套遍历中序 for j l1 - r1 ,找到s1[j] = s2[i] 那么找到根在中序中的位置 pos = j ;先序输出根cout<<s2[pos];
利用flag两层退出
那么层序的左右怎么在左子树和右子树里面写呢?
首先一定不可能确定中序的左右子树在层序的位置,它是跳跃的。
所以只能死办法全部传入,那么层序中第一个在子树中找到的值就是这颗树的根。
一些关于递归的想法:
如果你的dfs是没有 if条件的 dfs,意味在开头没有出口 if 结束不了
反之,可以不用。如果用了,就是减枝