(紫书自学系列)P150树的层次遍历(Trees on the level,UVa 122)(C++)

通过例题学习对树的相关操作,为BFS和DFS打好基础,以刘佬给的代码为准,加些解释以便理解。(本题刘佬提出内存泄漏的情况,本文未提及,若有需要请自行阅读书本)

文章目录


前言

第五章:大致码完书上STL的题(不会的看题解,最起码题解看懂,看不懂也没事)先理解STL的一些基本内容就好后面刷题再补

第六章:6.1和6.2 快速过一遍,看例题,自己动手敲敲就好了

6.3就是梦的开始,BFS与DFS用的地方很广,我个人认为这一章DFS和BFS最为重要,看的时候要扣好每个细节。


一、题目

树状结构在电脑科学的许多领域中都相当重要。本问题牵涉到建立树及遍历树。

 

下为输入输出的案例:

输入:

(11,LL) (7,LLL) (8,R) (5,) (4,L) (13,RL) (2,LLR) (1,RRR) (4,RR) ()
(3,L) (4,R) ()

输出:

5 4 8 11 13 4 7 2 1
not complete

二、问题分析

该题需要注意的是,我们不仅仅只是构造树然后遍历输出

题目指出,输入的数据可能不构成树(分为两种情况):

  • 有某些该有的节点没有给

  • 有某些节点重复给

数据读入即为一个难点

我们在读入节点的时候,将其存到树中,下面为“数据读入函数”

char s[maxn];

bool read_input()                                 
{
     failed = false;                               //
     root = newnode();                             //创造树的根节点
     for(;;)                                       //每组数据为一个循环,如(11,LL)
    {                                           
         if(scanf("%s", s) != 1) return false;     //scanf只要读入字符串,函数结果就为1,读入空则为0,使得每次循环处理一个数据
         if(!strcmp(s, "()")) break;               //结束标志
         int v;                                      
        sscanf(&s[1], "%d", &v);                   //该处sscanf函数为从字符串中提取第一个数字存到v中,即将要处理的数据给v
        addnode(v, strchr(s, ',')+1);              //addnode函数需要我们定义,将需要处理的数据按“,”后面的字母(如“L”)进行处理
    }
    return true;
}

关于树的建立如下

struct Node
{
    bool have_value;             //作为一个标记,判断该结点是否被赋值
    int num;                     //该结点存入的数据
    Node *left,*right;           
    Node()                       //养成结点初始化为空的习惯
    {
        have_value=false;
        left=NULL;
        right=NULL;
    };
};

在每次处理一个新数据时,我们需要对树进行扩展,因此,我们还需要一个构造树结点的函数(用到new函数,申请空间并构造函数,如果空间不足则返回NULL):

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

对于“数据读入函数”中的addnode函数,我们进行如下定义:

    void addnode(int v,char* s)                     //传入处理的数据v和字符串s(s是从原s的逗号后面第一个字符开始的)
    {
        int n=strlen(s);                            //n为长度
        Node* u=root;                               //根结点root的地址为u
        for(int i=0; i<n; i++)
        {
            if(s[i]=='L')                            //读到”L“时候,当前结点的左结点若为空,则建一个,当前的u为之前的u指向的左结点
            {
                if(u->left==NULL) u->left=newnode;
                u=u->left;
            }
            else
            {
                if(s[i]=='R')                         //读到”R“时同理
                {
                    if(u->right==NULL)
                        u->right=newnode();
                    u=u->right;
                }
            }
        }
        if(u->have_value) failed=true;                 //若该结点已被赋值
        u->num=v;                                       //当前u为数据v应该在的位置,数据v赋给它对应u结构体的数据num
        u->have_value=true;                         //别忘了标记该结点为已赋值
    }

最难的部分已经结束了,现在只要用队列实现BFS离AC不远了

    bool BFS(vector<int>& ans)                       //用ans来存输出数据
    {
        queue<Node*> q;
        ans.clear();                                 //ans清空别忘了
        q.push(root);                                //队列q,从根结点root开始排队
        while(!q.empty())
        {
            Noede* u=q.front();                      //对即将出队的头部结点的子结点进行遍历
            q.pop();
            if(!u->have_value) return false;         //如果该结点没有赋值,则说明输入有误
            ans.push_back(u->num);                     //父结点访问
            if(u->left!=NULL) q.push(u->left);       //如果左结点非空,则将其入队
            if(u->right!=NULL) q.push(u->right);
        }
        return true;
    }

三.完整代码

#include <iostream>
#include<vector>
#include<queue>
#include<cstring>
#define maxn 1001000
using namespace std;



struct Node
{
    bool have_value;
    int num;
    Node *left,*right;
    Node()
    {
        have_value=false;
        left=NULL;
        right=NULL;
    };
};
char s[maxn];
bool failed=false;
int num;
Node* root;
Node* newnode()
{
    return new Node();
}

bool BFS(vector<int>& ans)                       //用ans来存输出数据
{
    queue<Node*> q;
    ans.clear();                                 //ans清空别忘了
    q.push(root);                                //队列q,从根结点root开始排队
    while(!q.empty())
    {
        Node* u=q.front();                      //对即将出队的头部结点的子结点进行遍历
        q.pop();
        if(!u->have_value) return false;         //如果该结点没有赋值,则说明输入有误
        ans.push_back(u->num);                     //父结点访问
        if(u->left!=NULL) q.push(u->left);       //如果左结点非空,则将其入队
        if(u->right!=NULL) q.push(u->right);
    }
    return true;
}

void addnode(int v,char* s)                     //传入处理的数据v和字符串s(s是从原s的逗号后面第一个字符开始的)
{
    int n=strlen(s);                            //n为长度
    Node* u=root;                               //根结点root的地址为u
    for(int i=0; i<n; i++)
    {
        if(s[i]=='L')                            //读到”L“时候,当前结点的左结点若为空,则建一个,当前的u为之前的u指向的左结点
        {
            if(u->left == NULL) u->left=newnode();
            u=u->left;
        }
        else
        {
            if(s[i]=='R')                         //读到”R“时同理
            {
                if(u->right==NULL)
                    u->right=newnode();
                u=u->right;
            }
        }
    }
    if(u->have_value) failed=true;                 //若该结点已被赋值
    u->num=v;                                       //当前u为数据v应该在的位置,数据v赋给它对应u结构体的数据num
    u->have_value=true;                         //别忘了标记该结点为已赋值
}



bool read_input()
{
    failed =false;
    root = newnode();
    for(;;)
    {
        if(scanf("%s", s) != 1) return false;
        if(!strcmp(s, "()")) break;
        int v;
        sscanf(&s[1], "%d", &v);
        addnode(v, strchr(s, ',')+1);
    }
    return true;
}

int main()
{
    vector<int> ans;
    while(read_input())
    {
        if(failed||!BFS(ans))
        {
            cout<<"not complete";
        }
        else
        {
            for(vector<int>::iterator t=ans.begin(); t!=ans.end(); t++)
            {
                if(t!=ans.end()-1)
                {
                    cout<<*t<<" ";
                }
                else cout<<*t;
            }
        }
        cout<<endl;
    }

    return 0;
}

虽然很好理解树的概念,但一上手就写不了了

还得多练练,不能只看不练 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值