直接上题目:
以及输入输出样例:
分析
题目要求首先构造出树的结构类型,每个结点存储相应的字符串,再以字符串三种不同的类型为依据分别输出对应的字母。以字符串“11110100”为例,则树的根节点对应的字符串为“11110100”,字母为F,其两颗子树分别为“1111”(左子树)和“0100”(右子树),对应的字母分别为I和F,继续分解以此类推,知道不能再分解为止。
观察题目,第一行输入的N表示字符串的长度为2^N,第二行输入对应的字符串,题目要求我们输出的是含有F、B、I的字符串,而且是根据构造出来的树做后序输出。不难发现,题目要求我们构造的树是一颗完全二叉树,而实际上我们也不用关注数的结点对应了什么字符串,只需知道其对应的字母是F、B、I中的哪一个。我们先根据第二行输入的字符于树底层的2的N次幂个结点对应的结点中储存对应于F、B、I三个字母有关的信息。容易想到,一个结点对应的字母由其两个孩子对应的字母所决定(比如孩子分别是B、I,则父母结点就是F),我们就可以根据这个特点依次往上构造,直到构造出树的根结点为止。下面以字符串“1011”为例:
首先由于字符串为1011,则对应的底层的结点分别为I、B、I、I,其他如下图
分析上面例子,在输入时我们首先两个结点存储的是1和0,而需要构造的第一个父母结点也是由这两个结点决定,然后就是构造存储1、1的结点对应的父母结点。最后就是根据第二层的结点构造最后的根结点。
我们首先构造相应的树的结点的结构体:
#define maxsize 100
typedef struct BITNODE{
int num; //为了方便,我们不直接存储F、B、I三个字母,而是存储相应的数字,最后输出的时候在用switch进行转化
struct BITNODE* lchird, *rchird; //左孩子、右孩子
}BNODE;
不难发现,我们自下而上构造树的过程其实是一个后进后出的过程,因此我们可以构造一个队列,即我们根据队首两个元素(孩子结点)构造出一个新的元素(父母结点),然后将首两个元素出队,新元素入队,直到不能进行以上操作为止。
我们构造出以下的队列结构以及出队入队函数:
typedef struct treequeque {
BNODE* treeque[maxsize]; //我们直接构造树结点指针类型的数组
int head, tail; //定义队首队尾
}queque;
void inque(queque* p, BNODE* tree) { //入队函数
if (((p->tail) + 1) % maxsize == p->head) { //判断队是否满
printf("stack is full\n");
}
else {
p->treeque[p->tail] = tree;
p->tail = (p->tail + 1) % maxsize;
}
}
void outque(queque* p, BNODE** tree) { //出队函数
if (p->head == p->tail) { //判断队是否为空
printf("queque is blank\n");
}
else {
*tree = p->treeque[p->head];
p->head = (p->head + 1) % maxsize;
}
}
有了以上基础我们就可以开始构造完全二叉树了。
先是构造最底层的结点:
int i, all=1; //all为第二行输入的字符总数
scanf_s("%d", &i);
while (i > 0) { //计算All=2^i,这里就不直接调用函数了
all *= 2;
i--;
}
char c;
scanf_s("%c", &c, 2);
queque quetree; //定义队列quetree
quetree.head = 0; //初始化队头队尾
quetree.tail = 0;
for (i = 0; i < all; i++) {
BNODE* p = (BNODE*)malloc(sizeof(BNODE)); //动态内存分配构造结点
if (p) {
p->lchird = NULL;
p->rchird = NULL;
scanf_s("%c", &c,2);
if (c == '0') { //根据读入的字符对p->num赋值
p->num = 0;
}
else if (c == '1') {
p->num = 1;
}
inque(&quetree, p); //赋值完成后入队
}
}
到这里树的最底层的结点就构造好了。接下来,就是自下而上构造整棵树了。
BNODE* left, * right; //用于存储孩子结点地址
int k;
while ((quetree.head + 1) % maxsize != quetree.tail) { //当队列只有一个元素即根结点时循环结束
outque(&quetree, &left); //结点出队记为左孩子
outque(&quetree, &right); //结点出队记为右孩子
if (left->num == right->num) { //根据左右孩子确定k值,赋给父母结点
k = left->num;
}
else {
k = 2;
}
BNODE* p = (BNODE*)malloc(sizeof(BNODE)); //构造父母结点
if (p) {
p->lchird = left; //指向两个孩子
p->rchird = right;
p->num =k; //记录k的值
inque(&quetree, p); //父母结点入队
}
}
BNODE* a = quetree.treeque[quetree.head]; //最后用a记录树的根节点
到这里,整个树就构造完成了,最后就是树的后序输出,我们写一个递归函数解决:
void myprintf(BNODE* p) {
if (p->lchird) { //若左子树存在则用函数处理左子树
myprintf(p->lchird);
}
if (p->rchird) { //然后处理右子树
myprintf(p->rchird);
}
switch (p->num) //最后处理p自己
{
case 2:
printf("F");
break;
case 0:
printf("B");
break;
case 1:
printf("I");
break;
}
}
所以我们最后只需
myprintf(a);
即可。