Description
某校数据结构课程设计期末考核即将来临。为了考查学生对树结构的认识,同时也检验学生的编程能力,该校将考核的一项内容定为:要求编写程序按编号顺序打印出节点个数不少于m的所有二叉树。
编号规则:
· 仅有一个节点的树编号为1。
· 当满足以下条件之一时,定义二叉树a的编号比b大:
1. a的节点数比b多。
2. 若a的节点数与b相等,且a的左子树编号比b的左子树大。
3. a的节点数和左子树编号都和b相等,且a的右子树编号比b的右子树大。
二叉树的节点用大写X表示,例如:
当然当m较大时,检验答案对错的工作也是很繁重的,所以教师只打算对其中的若干个编号的二叉树进行抽查,请编制一个程序能够产生编号为n的二叉树的标准答案。
Input
输入数据由多组数据组成。每组数据仅一个整数,表示n (1≤n≤10^8)的值。输入数据以n=0表示结束,该数据不要处理。
Output
对于每组数据,输出仅一行,即你求出的标准答案。
二叉树的输出格式为:
(左子树){若左子树为空则省略}X{根}(右子树){若右子树为空则省略}
其中{…}中的内容是说明,不必输出。例如,在上图中编号为5的树可表示为X((X)X);编号为6的树表示为(X)X(X)。
Sample Input |
Sample Output |
20 0
|
((X)X(X))X |
这题主要难点在于如果构造出一种递归方法来输出输出这个图形,首先对于二叉树的种类我们能够想到卡特兰数,且题目对于树的排序首先是按照树节点的数量来排的。例如题目给定了第20,卡塔兰数的前几个为 1 2 5 14 于是我们就知道(1+2+5 < 20 < 1+2+5+14)第20个树在4个结点构成树中,于是我们需要一个函数,这个函数接受两个参数,一个是结点数,另外一个是打印第几种形态的树,于是在输入为20的情况下,进入的第一个函数的传值为 (4, 20-1-2-5),进去以后我们就要通过枚举左右两边的子树结点数来进一步确定这棵树的形态,还是以输入20为例,进去之后两个参数是 4, 12 那么我们根据这样的规律来枚举,当然我们枚举都是保证其序列从小到大。设左右子树的结点数分别是(a, b), 那么从(0, 3) -> (1, 2) -> (2, 1) -> (3, 0) (根节点本身占用一个节点)通过这样枚举我们总是保证左子树的结点尽可能的少,也就是说我们的枚举是有序的,每当枚举完一个我们就需要减去前面的这种构型,当出现负数的时候,我们就知道了左右节点的个数分别要为多少了。具体过程如下:(h[x]为卡特兰数,表示x个结点下共有多少种树形)
12 --> 枚举(0, 3) --> 12 - h[0]*h[3] = 7
7 --> 枚举(1, 2) --> 7 - h[1]*h[2] = 5
5 --> 枚举(2, 1) --> 5 - h[2]*h[1] = 3
3 --> 枚举(3, 0) --> 3 - h[3]*h[0] = -2
于是我们得到树的构型应该是左3右0,接下来就是计算左右子树的数的构型了,设余下的型号数为N,左子树结点数为L,右子树结点数为R,对于左子树构型为:
No_L = ceil (1.0*N / h[R]);
No_R = N % h[R], if (N%h[R] == 0) No_R = h[R];
这个公式是可以推出来的,上面的取上整表明了左子树的权值不满1的时候是要添为整数的,右子树的话就直接跟N对h[R]的余数挂钩。
代码如下:
#include <cstdlib> #include <cstring> #include <cstdio> #include <algorithm> #include <cmath> using namespace std; typedef long long int Int64; Int64 h[20]; int N; void pre() { h[0] = 1; for (int i = 1; i <= 20; ++i) { for (int j = 0; j <= i; ++j) { h[i] += h[j] * h[i-j-1]; } } } void draw(int tot, int no) // 传递两个参数,一个是需要多少个x,一个是打印多少号形态 { int t = no, L, R; for (int i = 0; t > 0; ++i) { t -= h[i] * h[tot-i-1]; if (t <= 0) { t += h[i] * h[tot-i-1]; L = (int)ceil(1.* t / h[tot-i-1]); R = t % h[tot-i-1]; if (!R) R = h[tot-i-1]; if (i) { printf("("); draw(i, L); printf(")"); } printf("X"); if (tot-i-1) { printf("("); draw(tot-i-1, R); printf(")"); } break; } } } int main() { pre(); int t; while (scanf("%d", &N), N) { t = N; for (int i = 1; t > 0; ++i) { t -= h[i]; if (t <= 0) { t += h[i]; draw(i, t); break; } } puts(""); } return 0; }