题意:给出一个二叉树的编号,问形态。
编号依据
1:如果二叉树为空,则编号为0;
2:如果二叉树只有一个节点,则编号为1;
3:所有含有m个节点的二叉树的编号小于所有含有m+1个节点的二叉树的编号;
4:如果一棵含有m个节点的二叉树(左子树为L,右子树为R)的编号为n,要想其它含有m个节点的二叉树的编号如果大于n,则需要满足两个条件中的任意一个:1、左子树的编号大于L的左子树的编号等于L的编号,但是右子树的编号大于R的编号。(大概就是先将右子树的个数填满将变幻完后再将右子树的点向左子树转移
一道卡特兰数的题。。总的来说代码难度不高,但是思维难度还是挺高的,首先二叉树形态有几种就很想然得想到用卡特兰数啦。
递推公式
$ cat[i]=cat[i-1]* ( 4 * i-2)/(i+1) \ \ \ \ \ cat[1]=1,cat[0]=1 $
不了解的自行百度卡特兰数
我们首先可以设一棵二叉树可能的形态种类数为 $ f[n] $ 那么对于左子数所含有的节点数 $ 0<=i<=n-1 $ 都有 $ f[n]= \sum_{i = 0}^{n-1}\ f[i] * f[n-i-1] $
首先我们设当前含有x个节点的二叉树第order个排序通给定数 $ n $ 计算出当前有几个节点并且当前是第几个排序 $ order=n-Catalan(0)+Catalan(1)+...+Catalan(i-1)) \ \ \ \ (Catalan(i)>=n)) $
然后我们再去考虑二叉树中左子树有多少个节点,右子树有多少节点。由题意知初始状态下,左子树为空,所有的节点均在右子树上并且所有节点只有右孩子,随着右子树的变化完后,右子树拿一个节点到左子树,然后再变化。这个过程就像时钟一样,右子树是分钟,左子树是时针,右子树的变化走满了,就开始进位,然后左子树再变化。当然这里使用的进制当然就是卡特兰数进制呀。然后只要算出左右此时的节点个数以及是第几个排序。之后层层递归下去即可。
代码
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=500000010;
int n;
long long cat[40],sum[40];
void star(){
cat[0]=cat[1]=1;
for(int i=2;i<35;++i){
cat[i]=cat[i-1]*(4*i-2)/(i+1);
}
return;
}
void find(int x,int order){
if(x==1){
printf("X");
return;
}int i,cnt=0;
for(i=0;cnt<order;i++){//保证左子树尽可能小
cnt+=cat[i]*cat[x-i-1];
}i--;
int l=i,r=x-l-1;
int neworder=order-(cnt-cat[l]*cat[r]);//减去其中的个数
if(l>0){
printf("(");
find(l,(neworder-1)/cat[r]+1);//减一然后在后面加一相当于判断是否有余数,有余数加一
printf(")");
}printf("X");
if(r>0){
printf("(");
find(r,(neworder-1)%cat[r]+1);//同上
printf(")");
}
}
int main(){
star();
while(scanf("%d",&n)&&n){
int i,cnt=0;
for(i=1;cnt<n;++i){
cnt+=cat[i];
}i--;
find(i,n-(cnt-cat[i]));
printf("\n");
}
return 0;
}