【专题】二叉树
这两天的任务是二叉树_(:з」∠)_
首先是理论学习部分。
子树满二叉树完全二叉树这类基础概念就不介绍了,这里只记一些我觉得老记不住的:
1.平衡二叉树——平衡二叉树又被称为AVL树(区别于AVL算法),它是一棵二叉排序树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
主要的性质:
二叉树性质
(1) 在非空二叉树中,第i层的结点总数不超过 , i>=1;
(2) 深度为h的二叉树最多有 个结点(h>=1),最少有h个结点;
(3) 对于任意一棵二叉树,如果其叶结点数为N_0,而度数为2的结点总数为N_2,则N_0=N_2+1;
(4) 具有n个结点的完全二叉树的深度为![](https://gss3.bdstatic.com/-Po3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D76/sign=73aa05824f086e066ea83d4d03088be8/2fdda3cc7cd98d10cada9582223fb80e7bec9073.jpg)
![](https://gss3.bdstatic.com/-Po3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D76/sign=73aa05824f086e066ea83d4d03088be8/2fdda3cc7cd98d10cada9582223fb80e7bec9073.jpg)
(5)有N个结点的完全二叉树各结点如果用顺序方式存储,则结点之间有如下关系:
若I为结点编号则 如果I>1,则其父结点的编号为I/2;
如果2*I<=N,则其左儿子(即左子树的根结点)的编号为2*I;若2*I>N,则无左儿子;
如果2*I+1<=N,则其右儿子的结点编号为2*I+1;若2*I+1>N,则无右儿子。
(6)给定N个节点,能构成h(N)种不同的二叉树。
h(N)为卡特兰数的第N项。h(n)=C(2*n,n)/(n+1)。
(7)设有i个枝点,I为所有枝点的道路长度总和,J为叶的道路长度总和J=I+2*i
二叉树光是看理论的话其实没什么用。所以接下来上例题
1.FBI树
题目描述
我们可以把由“0”和“1”组成的字符串分为三类:全“0”串称为B串,全“1”串称为I串,既含“0”又含“1”的串则称为F串。
FBI树是一种二叉树,它的结点类型也包括F结点,B结点和I结点三种。由一个长度为2^N的“01”串S可以构造出一棵FBI树T,递归的构造方法如下:
1) T的根结点为R,其类型与串S的类型相同;
2) 若串S的长度大于1,将串S从中间分开,分为等长的左右子串S1和S2;由左子串S1构造R的左子树T1,由右子串S2构造R的右子树T2。
现在给定一个长度为2^N的“01”串,请用上述构造方法构造出一棵FBI树,并输出它的后序遍历序列。
输入输出格式
输入格式:
第一行是一个整数N(0 <= N <= 10),第二行是一个长度为2^N的“01”串。
输出格式:
包括一行,这一行只包含一个字符串,即FBI树的后序遍历序列。
输入输出样例
说明
对于40%的数据,N <= 2;
对于全部的数据,N <= 10。
这应该是比较经典的一道二叉树题了吧。比较简单,但这里主要想说一个技巧。
这道题主要是考察求后序遍历。我们知道,后序遍历是左右根,但用递归其实是不好实现的。原因如下:一般来说,递归都是从最上面的根开始,有了父亲接下来才会搜到儿子,那么就不可能显得出两个儿子再反推出父亲。解决方法其实也很简单,就是完全反过来储存。也就是说,后序遍历是左右根,那么我们在查找的时候就按根右左的顺序储存,这样最后从后往前输出就行了。
感觉这个技巧在二叉树里还挺有用的,见到好几次了。(至少在求x序遍历上是用得到的)
另外,这道题在做的时候虽然思路很顺,但是递归退出的条件调了好久都没调对。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cmath> 3 #include <cstring> 4 #include <cstdio> 5 #include <cstdlib> 6 #include <algorithm> 7 using namespace std; 8 string s,ans; 9 bool ok=true; 10 void build(int l,int r) 11 { 12 if(!ok) ok=true; //这里不能再return了,不然最后一层的左儿子就无法输出了 13 if(l>r) return ; 14 bool B=true,I=true; 15 for(int i=l;i<=r;i++) 16 { 17 if(s[i]=='0') I=false; 18 if(s[i]=='1') B=false; 19 if(!B && !I) break; 20 } 21 if(!B && !I) ans+='F'; 22 else if(B) ans+='B'; 23 else ans+='I'; 24 if(l==r) {ok=false;return ;} 25 /*如果当前是叶子结点,就不能再一分为二了。此时应返回上一层(其父亲):若当前为右儿子,则接下来搜左儿子;若当前是左儿子,则接下来再后退搜另一个节点 */ 26 build((l+r)/2+1,r); //先右 27 build(l,(l+r)/2); //后左 28 } 29 int main() 30 { 31 int n; 32 scanf("%d",&n); 33 cin>>s; 34 build(0,s.size()-1); //对于string类型:size()可用来求长度。但要注意直接cin的话是从0开始的 35 for(int i=ans.size()-1;i>=0;i--) cout<<ans[i]; 36 return 0; 37 }