【数据结构/递归】P1087 FBI树

题目描述
我们可以把由“0”和“1”组成的字符串分为三类:全“0”串称为B串,全“1”串称为I串,既含“0”又含“1”的串则称为F串。

FBI树是一种二叉树,它的结点类型也包括F结点,B结点和I结点三种。由一个长度为2^N
的“01”串S可以构造出一棵FBI树T,递归的构造方法如下:

T的根结点为R,其类型与串S的类型相同;
若串S的长度大于1,将串S从中间分开,分为等长的左右子串S_1
和S_2 ;由左子串S_1构造R的左子树T_1,由右子串S_2构造R的右子树T_2。
现在给定一个长度为2^N的“01”串,请用上述构造方法构造出一棵FBI树,并输出它的后序遍历序列。

输入格式
第一行是一个整数N(0≤N≤10),
第二行是一个长度为2^N的“01”串。

输出格式
一个字符串,即FBI树的后序遍历序列。

输入输出样例
输入 #1
3
10001011
输出 #1
IBFBBBFIBFIIIFF
说明/提示
对于40%的数据,N≤2;

对于全部的数据,N≤10。

noip2004普及组第3题



这道题是二叉树的题中比较有意思的一道,难度不大。从FBI这个充满杀气的名字里就能看出它有多么的霸(搞)气(笑)。
楼上把各种建树、遍历的方法都说得很详细了。这里我主要讲一讲结构体、“枚举类型(enum)”和string的子串的使用。代码可能稍微冗长,但是思路非常清晰,非常适合初学者。
拿样例数据来说,图片如下:

这里我采用了从上到下从左向右编号,假设输入的第一个参数为n,可以发现:


1.编号在闭区间

[[2 ^ n,2 ^ {n+1}-1] ]

的结点都是叶结点,没有左右子结点。

2.编号在半开半闭区间

[[0,2^n) ]

的结点是非子结点,第i个结点(i在上述范围内)的左、右孩子编号为

[2i,2i+1. ]

3.没有只有一个孩子的结点。


这是所有满二叉树的共同性质。这两个性质非常重要,一会要用到。
我的思路是,首先构建一个结构体,用来存储树上每个结点。这里,我的上面说的“枚举类型(enum)”就派上用场了。


枚举类型(又叫枚举变量)是一种数据类型,在C++中对应关键字enum。它用来表示一些具有同样性质的变量的集合体。和数组略有不同,枚举类型更像是一种符号。它的声明方式为:

enum agent{Vanguard,Sniper,Guard,Caster,Defender,Medic,Specialist,Supporter};

有人能看出来这是什么吗,有惊喜的

其中列举了几个变量,它们具有各自的值。一般来说,第一个变量的值是0,后面的递增。你也可以指定每个变量的值:

enum agent{Vanguard=1,Sniper,Guard,Caster,Defender,Medic,Specialist,Supporter};

这样第一个元素的值是1,后面的递增。

声明一个枚举类型的变量:

enum agent{Vanguard,Sniper,Guard,Caster,Defender,Medic,Specialist,Supporter};
int main()
{
	agent Amiya=Caster;
	cout<<Amiya;
}

输出为3。因为对于上述枚举类型,第一个元素是0,后面每个递增(是前一个的数值+1),可以得出Caster的值是3。

enum还有很多其他的用法。关于更多细节可以查看->某不知名官网关于enum的介绍

其实是我懒得


对于这个题,每个结点就是有F、B、I三种状态,用枚举类型再好不过了。不过要注意,直接输出枚举变量会输出数字,需要用switch转为字符。

enum type{F=1,B,I};

然后构建结构体:

struct node
{
	type t//枚举类型;
	int left//左儿子编号;
	int right//右儿子编号;
    //对于叶结点,它们的值都是0。这也是在递归遍历时停止的条件。
	void node_init(string s)//用字符串初始化每个结点的属性
	{
		bool have1=false,have0=false;
		for(int i=0;i<s.length();i++)//按照题意
		{
			if(s[i]=='0') have0=true;
			if(s[i]=='1') have1=true;
		}
		if(have0==true&&have1==false) t=B;
		else if(have0==false&&have1==true) t=I;
		else t=F;//上面三行,type型枚举变量被赋值。
	}
}a[2050];//题目中限定n<=10,加上根节点的一层就是11层,共有2048个结点。

我们还需要一个函数来初始化各个结点的儿子情况,赋值规则上面已经提到:

int n;
void init()
{
	for(int i=1;i<(int)pow(2,n);i++)
	{
		a[i].left=i*2;
		a[i].right=i*2+1;
	}
	for(int i=(int)pow(2,n);i<=pow(2,n+1);i++)
	{
		a[i].left=a[i].right=0;
	}
}

然后是递归地使用一个字符串和一个结点的编号来设定每个结点的FBI值。递归到叶节点时直接返回,表示当前递归道路搜索完毕:

void make_tree(string s,int code)
{
	if(code!=0)//如果一个结点是叶结点,那么它的孩子编号都是0,初始化时就能体现这一点。
	{
		a[code].node_init(s);//用当前字符串初始化
		make_tree(s.substr(0,s.length()/2),a[code].left);//截取左半部分
		make_tree(s.substr(s.length()/2,s.length()/2),a[code].right);//截取右半部分
	}
}

这里的substr函数是string的特有函数。用来截取一个string对象里的某一段。用法:

substr(<开始截取的字符串开头下标>,<截取字符串的长度>);

例如:

string s="012345";
cout<<string.substr(0,3);//从0开始,截取长度为3的字符串

输出:012


它可以轻松的分割一个串,比char字符串好多了!


最后是愉快的后序遍历。就像刚才强调的那样,要使用switch来对应地输出字符,而不是直接输出变量:

void back(int code)
{
	if(code!=0)
	{
		back(a[code].left);
		back(a[code].right);
		switch(a[code].t)
		{
			case F://也可以是case 1
				cout<<"F";
				break;
			case B:
				cout<<"B";
				break;
			case I:
				cout<<"I";
				break;
		}
	}
}

最后,愉快地写主程序,完美地AC!

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>//pow(幂)函数,调用数学库
using namespace std;
enum type{F=1,B,I};
struct node
{
	type t;
	int left;
	int right;
	void node_init(string s)
	{
		bool have1=false,have0=false;
		for(int i=0;i<s.length();i++)
		{
			if(s[i]=='0') have0=true;
			if(s[i]=='1') have1=true;
		}
		if(have0==true&&have1==false) t=B;
		else if(have0==false&&have1==true) t=I;
		else t=F;
	}
}a[2050];
int n;
void init()
{
	for(int i=1;i<(int)pow(2,n);i++)
	{
		a[i].left=i*2;
		a[i].right=i*2+1;
	}
	for(int i=(int)pow(2,n);i<=pow(2,n+1);i++)
	{
		a[i].left=a[i].right=0;
	}
}
void make_tree(string s,int code)
{
	if(code!=0)
	{
		a[code].node_init(s);
		make_tree(s.substr(0,s.length()/2),a[code].left);
		make_tree(s.substr(s.length()/2,s.length()/2),a[code].right);
	}
}
void back(int code)
{
	if(code!=0)
	{
		back(a[code].left);
		back(a[code].right);
		switch(a[code].t)
		{
			case F:
				cout<<"F";
				break;
			case B:
				cout<<"B";
				break;
			case I:
				cout<<"I";
				break;
		}
	}
}
string m;
int main()
{
	cin>>n;
	cin>>m;
	init();
	make_tree(m,1);
	back(1);
	return 0;
}


本人写这篇题解弄到晚上10:40,看在这么辛苦的份上,点个赞再走呗~(✪ω✪)

原文地址:https://www.cnblogs.com/jiangyuechen/p/12748649.html