uva122树的层次遍历全过程详解-紫书题分析

原题链接-洛谷
代码是模仿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之前,先把节点用结构体定义一下

struct Node{
    bool have_value;//因为题给数据可能存在重复给值的情况,所以要记录一下节点是否已经有值,如果已经有值还要
    //给值的话,就直接让failed赋值为true
    int val;
    Node *lefti,*righti;
    Node():have_value(false),lefti(NULL),righti(NULL){}//构造函数,用来赋初值 
};

我们之前分析过,由于树可能很“稀疏”,所以空间是动态申请的,这里写一个申请节点内存空间的函数

Node* new_node(){
    return new Node();
}

再把根节点先申请出来

Node* root;

现在开始写addnode
该函数的两个参数分别是值和位置,我们只要根据描述位置的字符串找到相应位置,如果相应位置上没有被赋值,就给它赋相应值。
先写伪代码

void add_node(int v,char* s){
    int n=strlen(s);
    Node* u=root;//从根节点开始找起
    int i;
    for(i=0;i<n;i++){
          如果是L就向该节点左子树找,R就向右找,如果要找的方向暂时没有申请过节点,就先申请出来,但不一定赋值。
        比如,如果在只有根节点的情况下,但是要加入的节点在LLL位置,这时中间是断开的,那我们就直接在这一步将根
        节点左子节点L,左子节点的左子节点LL都申请出来,但是这两个先不赋值,也不标记已经赋值,只给LLL赋值。
    }
    if(u->have_value) failed=true;//这三句是判断是否已经有值
        u->val=v;
        u->have_value=true;//注意赋完值之后标记一下
}

分析清楚之后,完成addnode的完整代码

void add_node(int v,char* s){
    int n=strlen(s);
    Node* u=root;
    int i;
    for(i=0;i<n;i++){
        if(s[i]=='L'){
            if(u->lefti == NULL) u->lefti = new_node();
            u=u->lefti;
        }
        else if(s[i]=='R'){
            if(u->righti == NULL) u->righti =new_node();
            u=u->righti;
        }
        
    }
    if(u->have_value) failed=true;
        u->val=v;
        u->have_value=true;
}

这样就完成了建树的代码了,之后我们就可以通过根节点找到找到任意一个子节点了。

八、接下来一步就是用bfs对整棵树进行遍历,bfs过程就不用多说了,这个比较简单。

bool bfs(vector<int>& da_an){
    queue<Node*> q;
    da_an.clear();
    q.push(root);
    while(!q.empty()){
        Node* u=q.front();
        q.pop();
        if(!u->have_value) return false;//注意这里,如果存在没有赋值的节点,那就说明树从中间“断”掉了,那就是非法的。
        da_an.push_back(u->val);
        if(u->lefti != NULL) q.push(u->lefti);
        if(u->righti != NULL) q.push(u->righti);
    }
    return true;
}

九、下面贴出整个代码供大家参考

#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn=1001005;

//pre_define

//define Node
struct Node{
    bool have_value;
    int val;
    Node *lefti,*righti;
    Node():have_value(false),lefti(NULL),righti(NULL){} 
};

//define var
bool failed;
Node* root;
char s[maxn];

//pre_var
Node* new_node(){
    return new Node();
}

void remove_tree(Node *u){//这个是递归释放空间的过程
    if(u==NULL) return;
    remove_tree(u->lefti);
    remove_tree(u->righti);
    delete u;
}

void add_node(int v,char* s){
    int n=strlen(s);
    Node* u=root;
    int i;
    for(i=0;i<n;i++){
        if(s[i]=='L'){
            if(u->lefti == NULL) u->lefti = new_node();
            u=u->lefti;
        }
        else if(s[i]=='R'){
            if(u->righti == NULL) u->righti =new_node();
            u=u->righti;
        }
        //if(u->have_value) ok=true;
        //u->val=v;
        //u->have_value=true;
    }
    if(u->have_value) failed=true;
        u->val=v;
        u->have_value=true;
}

bool bfs(vector<int>& da_an){
    queue<Node*> q;
    da_an.clear();
    q.push(root);
    while(!q.empty()){
        Node* u=q.front();
        q.pop();
        if(!u->have_value) return false;
        da_an.push_back(u->val);
        if(u->lefti != NULL) q.push(u->lefti);
        if(u->righti != NULL) q.push(u->righti);
    }
    return true;
}

//jie_xi_biao_da_shi;
bool read_tree(void){
    failed=false;
    remove_tree(root);
    root=new_node();
    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;
}

int main(){
    
    vector<int> da_an;
    while(read_tree()){
        if(failed || !bfs(da_an)) cout<<"not complete"<<endl;
        else{
            for(vector<int> :: iterator it=da_an.begin(); it != da_an.end();it++){
                cout<<*it;//在这里用到了stl的迭代器,这种用法紫书前面的题有讲解过。
                if(it!=da_an.end()-1) cout<<' ';
            }
            cout<<endl;
        }
    }
    return 0;
}

你可能感兴趣的:(简单树型结构,bfs)