原题链接-洛谷
代码是模仿lrj大神的,在这里主要写一些自己的理解和分析,希望可以帮助大家理解这道题。
本题的最大节点数为256,且并未指出最大深度,所以如果用编号思想的话,要编号到2的256次方,我们都知道这是个天文数字,即使用高精度也是不好做的。而且我们完全没有必要用到编号,因为即使编号了,这课树的绝大多数编号也都是没有被使用的,(最坏情况下是大概是2的256次方-256个未使用),所以可以说这棵树是很稀疏的一棵树。
那么我们就只能用另外一种方法了,就是用几个节点就申请多大的空间。更好的事情是,我们可以在最后释放这些空间。
以下是代码实现过程
一、分析题目的输入组数
我们发现可能出现多组数据输入输出的情况,遇到这种情况时,我们的主函数模板一般是这样的:
情况一(没有事先给出输入组数的时候):
while(输入函数返回值为TRUE){
将答案保存起来,然后输出,或者直接输出。
}
这里的输入函数可能是scanf语句或者cin,比如有些题会说明以-1为结束,可以这样写
while(scanf("%d",&k)==1 && k!=-1);
也可能是m,n两个,同样道理。
这个输入函数也可以是根据具体题目情况自定义的函数,比如这道题我采用的就是自定义函数。
情况二(给出输入组数):
这个比较简单,可以先输入组数t,然后while(t --){}即可。
二、分析基本解题思路
1、因为不能编号,所以我们采用自定义的结构体链接整棵树。
2、输入方式我们用自定义的输入函数来解决。
3、先建树,再bfs。
4、很多同学可能思考到这一步就比较凌乱,不知道之后该怎么做,这时我推荐我们可以一开始不要思考太多,可以先开始做题,然后慢慢发现问题,慢慢把代码写出来。
三、直接先能写多少写多少,先写出主函数的框架。
下面是伪代码:
int main(){
//申请一个数组用来存答案;
while(自己写的读入数据的函数(),读入同时就可以建树){
bfs;
输出数据;
}
return 0;
}
四、进一步思考,如何来读取数据
这时看输入样例,我们发现所有的插入的结点之间都是空格隔开的,这时推荐用scanf来写,并且每一组都有唯一的结束标志(),所有输入彻底结束的标志是“没有标志”,也就是没有任何数据继续读入时,读入数据要立即结束。也可以理解为scanf返回值非1(就是读不到东西了)所以框架继续搭建。
char s[1001005];
bool read_tree(void){
while(true){
if(scanf("%s",s)!=1) return false;//如果读不到东西,返回false,这样主函数的循环就结束了
if(!strcmp(s,"()")) break;//如果遇到括号,只代表这一组数据结束。
解析输入格式并处理数据...
}
return true;
}
这样就能把数据读进来了,虽然只是存在字符数组中,还没有进行解析,但是我们这个时候可以把我们读到的东西试着原样输出一下,调试一下有没有问题,对于这种比较复杂的程序,逐个调试子函数是很有必要的,这样可以尽量避免写完所有之后,找半天不知道到底哪个地方有错。
五、对于读入的数据进行解析
我们之前只把数据读在了字符数组里面,但是这道题的目标是把依靠数据建树,所以我们要进行解析。首先我们可以很直观地看到我们读的数据分为两个部分,以输入样例为例子,我们读进来的第一个s的内容是(11,LL),它的两个部分是11 和 LL 括号和逗号都是我们不要的。
所以我们现在来解析s数组。
1、解析出新节点的权值: 这时我们要先学习一个新的知识,sscanf函数,我的理解是,它的作用是将其第一个参数当做用户输入的内容来进行输入,后两个参数和我们熟悉的scanf函数的两个参数无异。
以(11,LL)为例,11是我们要加的点的权值,我们定义一个变量叫做val来存它(value的简写),然后请读者仔细理解这个语句:
sscanf(&s[1],"%d",&val);
其实就相当于我们用scanf语句读入数据时,用户写在那个黑框里面敲的是11,LL) 一样。这个语句读了11,舍弃了其它的,因为其它不是整型数据。有同学会问,为什么少了个括号。注意看这一句里面是s[1],代表是从s的第一个而不是第0个字符开始的。另外要记得加取地址符号。
2、接下来要解决的是解析出位置,就是LL部分,我们又要学习一个新知识,strchr函数,它有两个参数,第一个是字符串,第二个是字符,所以它的名字很好记,string和char的结合,strchr,作用是返回指定字符串中第一个该字符的地址。注意这个表达式:
strchr(s,’,’)+1
这个运算结果会返回s字符串中逗号地址再加一,也就是(11,LL)例子中的第一个L的地址。
有了这个地址,我们就能找到s字符串中逗号之后的内容了,至此我们就完成了对读入数据的解析。接下来我们进一步完善我们的读入数据函数。
六、完善读入数据函数:
bool read_tree(void){
failed=false;
while(true){
if(scanf("%s",s)!=1) return false;
if(!strcmp(s,"()")) break;
int val;
sscanf(&s[1],"%d",&val);
add_node(val,strchr(s,',')+1);
}
return true;
}
我们把解析出的两个数据作为参数传递给了名叫add_node的函数,这个函数将帮助我们给树里面添加节点,这时我们假设已经写出了这个函数,先把这句写在这里,等一会再完成这个函数。
但是还要注意这道题里的一个坑,就是题目之中读入的数据可能是非法的,这个时候需要我们进行判断,我们定义变量failed为false代表合法,failed为true代表非法,我们每次刚开始读入时先假设它是合法的,之后failed的值可能会改变,这个要根据具体这道题的非法条件来看,我们暂时不管这个,先把failed赋值为false;
七、有了节点的两个参数(权和位置),就可以写addnode函数了
在写addnode之前,先把节点用结构体定义一下