题意:
如图编号,结点数依次增加,每次先对左子树进行递归操作,再对右子树进行递归,用X表示一个节点,每一对括号表示一棵树,以 (左子树)X(右子树) 的形式输出这棵树,当然如果是空树就不输出, 求编号为n的二叉树
思路:
1.先还原二叉树形态,后递归
-
1 首先二叉树的种类数满足卡特兰数,所以有递推关系可得出任意结点的卡特兰数ct[],根据编号找到它的结点数,和在该结点数下的第几棵树。
-
2 由编号还原二叉树形态,右子树满足卡特兰数满状态时,左子树移动一下,直到左子树满足它的卡特兰数。
-
3 递归画图,递归框架,从根节点开始画,第一层传参时,不输出左右括号,其余结点,只要存在就要有右括号,画完左边画’X’画右边
f(LL x,bool flag=1)
{
if(flag)
putchar('(');
f();
putchar('X');
f();
if(flag)
putchar(')');
}
然后思路理好后贴代码,有些地方是挺难想明白,画个图就慢慢清楚了
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<string.h>
#include<cstdlib>
#include<cstring>
#include<math.h>
#include<cmath>
#include<vector>
#include<map>
#include<queue>
#define LL long long
#define Pr pair<int,int>
using namespace std;
int n,m;
LL ct[233];
LL cts[233];
void init()
{
ct[0]=1;
cts[0]=0;
for(int t=1;t<33;t++)
{
ct[t]=ct[t-1]*(4*t-2)/(t+1);
cts[t]=cts[t-1]+ct[t-1];
}
}
void f(LL x,bool flag=1)
{
if(x==0)
return ;
LL l,r;
int pos;
for(int i=0;;i++)
{
if(x<cts[i])
{
pos=i-1; //结点
break;
}
}
l=0;
r=pos-1;
x-=cts[pos]; //位于pos结点卡特兰数的第x位
LL p1=0;
while(x-ct[r]>=0)//还原二叉树的形态
{
x-=ct[r];
p1++; //左子树形态+1
if(p1==ct[l])
{
l++;
r--;
p1=0;
}
}
if(flag)
putchar('(');
f(cts[l]+p1);
putchar('X');
f(cts[r]+x);
if(flag)
putchar(')');
}
int main()
{
init();
LL x;
while(scanf("%lld",&x)!=EOF)
{
if(x==0)break;
f(x,0);
cout<<endl;
}
return 0;
}
2.对左子树为空,右子树为空,左右子树非空递归
- 1 一样推出这棵树有多少个节点,以及是当前第几个编号
- 2 当编号小于ct[结点-1]时,左子树此时为空,往下递归,结点数减一,编号不变(如图结点为4时的第二棵,在结点为3时依旧为第二棵)
- 3 当编号大于ct[结点]-ct[结点-1],右子树为空,递归时所在编号需要减去(左子树为空+左右子树非空)的树的数目,即n-(ct[结点]-ct[结点-1])
- 4 左右子树均非空时,先推出右子树的节点数
//pos为当前结点数
int t;
for(int i=pos-1;i>=0;i--) //例如pos为4,左右子树结点和为3,枚举i=3,2,1,i为右子树数目
{
if(n>ct[i]*ct[pos-1-i]) //有 ct[3]*ct[0] , ct[2]*ct[1] ,ct[1]*ct[2] , ct[0]*ct[3]
n-=ct[i]*ct[pos-1-i]; //假设取到第二段,右子树结点为2,左子树结点为1 ,n为在第二段中的编号
else
{
t=i;//推出右子树的结点数
break;
}
}
- 5 分别递归左右子树
printf("(");
f(n/ct[t]+(n%ct[t]!=0),pos-1-t);//递归左子树,每进行一次右子树的ct[],左子树形态加1,别忘了后面非整除再+1
printf(")X(");
f((n-1)%ct[t]+1,t);//递归右子树,取余得到右子树形态,编号保证不为0
printf(")");
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<string.h>
#include<cstdlib>
#include<cstring>
#include<math.h>
#include<cmath>
#include<vector>
#include<map>
#include<queue>
#define LL long long
#define Pr pair<int,int>
using namespace std;
int n,m;
long long ct[233]; //要long long 类型
void init()
{
ct[0]=1;
for(int t=1;t<33;t++)
{
ct[t]=ct[t-1]*(4*t-2)/(t+1);
}
}
void f(int n,int pos)//有pos个结点,是其中的第n个编号
{
if(pos==1)
{
printf("X");
return ;
}
if(n<=ct[pos-1])//左子树为空树
{
printf("X(");
f(n,pos-1);
printf(")");
}
else if(n>ct[pos]-ct[pos-1])//右子树为空树
{
printf("(");
f(n-(ct[pos]-ct[pos-1]),pos-1);
printf(")X");
}
else //左右子树都是非空
{
int t;
for(int i=pos-1;i>=0;i--)
{
if(n>ct[i]*ct[pos-1-i])
n-=ct[i]*ct[pos-1-i];
else
{
t=i;//推出右子树的结点数
break;
}
}
printf("(");
f(n/ct[t]+(n%ct[t]!=0),pos-1-t);
printf(")X(");
f((n-1)%ct[t]+1,t);
printf(")");
}
}
int main()
{
init();
int x;
while(scanf("%d",&x)!=EOF)
{
if(x==0)
break;
for(int i=1;i<33;i++)//推出这棵树的节点数
if(x>ct[i])
x-=ct[i];
else
{
f(x,i);
break;
}
cout<<endl;
}
return 0;
}