题目链接POJ 1095 Trees Made to Order
题意:对二叉树按一定规则编号,对给定编号N(1 <= n <= 500,000,000),要求按格式输出此二叉树。
解析:这是碰到的第一个卡特兰数相关的题,不过涉及不深,只要稍微了解就足够了。算法就是对给定编号,由卡特兰数推出其左右子树编号即可递归得解。难点就是左右子数编号的求解,网上的题解大都是直接应用了如我代码中的公式,并未解释公式的来历,这给像我这样的新手造成了不小的困扰。
公式详解:由编号的规则我们可以知道,在左右子树节点数不变的情况下,左子树按规则加一就相当于右子树按规则走完全过程,相当与进位,所以才有代码中‘/’与‘%’的关系,不过具体细节还是稍微麻烦点,我悲催的推了一下午。余下的就看代码吧
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int catalan[] = {1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, 2674440, 9694845, 35357670, 129644790, 477638700, 1767263190};
//输出序号为n的树(有k个节点)
void Slove(int n){
int ln,rn;
int i;
int k;
if(n == 0){
return ;
}
//以下循环判断节点个数k
for(k = 1; ;k ++){
if(n - catalan[k] > 0){
n -= catalan[k];
}
else {
break;
}
}
if(k == 1){
printf("X");
return ;
}
//分别得出左右子树节点数i,k - i - 1
for(i = 0;i < k;i ++){
if(n - catalan[i]*catalan[k - 1 - i] <= 0){
break;
}
else {
n -= catalan[i]*catalan[k - 1 - i];
}
}
//难点!!
//左右子树的值,此值是此树在节点为i(或 k - i - 1)的树中编号
//在节点数不变的前提下
//左子树按规则+1,相当于右子树从最小到最大的一轮变化
//所以有/和%的运算得出子树的值
ln = n/catalan[k - 1 - i] + (n%catalan[k - i - 1] != 0);
rn = (n - 1)%catalan[k - 1 - i] + 1;
if(i){
int temp = 0,j;
for(j = 1;j < i;j ++){
temp += catalan[j];
}
printf("(");
//ln + temp 是此子树在全局中的编号
Slove(ln + temp);
printf(")");
}
printf("X");
if(k - i - 1){
int temp = 0,j;
for(j = 1;j < k - 1 - i;j ++){
temp += catalan[j];
}
printf("(");
Slove(rn + temp);
printf(")");
}
}
int main(){
int n;
while(scanf("%d",&n),n){
Slove(n);
printf("\n");
}
return 0;
}