FBI树解析

直接上题目:
在这里插入图片描述
以及输入输出样例:
输入输出样例

分析

题目要求首先构造出树的结构类型,每个结点存储相应的字符串,再以字符串三种不同的类型为依据分别输出对应的字母。以字符串“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,其他如下图

决定
决定
决定
决定
决定
决定
I
I
I
F
B
F
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);

即可。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值