uva122--二叉树的层次遍历

本文介绍了一种根据特定输入格式构建二叉树的方法,并实现了层次遍历输出。通过递归方式创建结点,利用BFS实现遍历。特别讨论了sscanf函数的多种实用技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


题意:

输入一棵二叉树,输入形式是给定一个结点值和一个字符串,如果字符串时L,则表示该节点在上一层相邻结点的左边,若是R,则表

示该节点在上一层相邻结点的右边,若没有字符串则表示该节点就是根节点,字符串和节点值用小括号括起来,并且相邻括号之间都

有空格隔开,空括号()则表示输入结束。然后要求你层次输出二叉树结点的值。注意:如果从根节点到某个叶节点的路径有的结点

没有给出,或者给出的超过一次,应当输出-1,结点的个数不应该超过256个。



input:

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

(3,L) (4,R) ()

output:

5 4 8 11 13 4 7 2 1 

-1



分析:

由于题意中并没有明确指出二叉树是一个满二叉树,而且结点数最大是256个,所以不能考虑将二叉树的结点作为编号放进数

组中,不能开那么大的数组,所以只能根据需要建立新的结点,然后再将结点组织成一棵树。首先需要定义一个node 的结构,

并对应整棵二叉树的树根root。然后是写个测试函数,测试根节点,左子树和右子树是否为空,再就是重头戏了,建树函数,由于

是递归建树,先从根节点开始,如果左子树为空,就建立结点,建树。右子树的操作一样,如果根节点为空,记录下来,并给它

赋值,相当于创建结点了。建树过程结束后,就是BFS层次遍历二叉树了,可以将二叉树的结点放进一个数组中去,边放边输,一

开始是一个根节点 ,然后输出一个结点,就把它的左右结点(如果存在)都放进去。过程详见代码,最后一步就是主函数的输入

了,本题的输入,是将其看成了一个字符串,并且巧妙的运用了sscanf()函数,详细见程序,下面上程序:



代码:

#include <bits/stdc++.h>
using namespace std;

typedef struct node //结点内容
{
    int V;
    node *L;
    node *R;
}tree;
tree node[300];
tree *root;
int test(tree *root) //测试根节点,和左、右子节点是否为空
{
    if(!root->V) //根节点为空
        return 0;
    int ans=0;
    if(!root->L||test(root->L))
        ans++;
    if(!root->R||test(root->R))
        ans++;
    return ans==2; //左右子节点为空
}

int tree_size,complete; //tree_size编号,complete记录结点是否为空
int madetree(tree *root,char *str,int v) //建树过程
{
    if(*str=='R')
    {
        if(!root->R) //右子树为空,建立结点
        {
            root->R=&node[++tree_size];
        }
        madetree(root->R,str+1,v); //建树
    }
    if(*str=='L')
    {
        if(!root->R)
        {
            root->R=&node[++tree_size];
        }
        madetree(root->L,str+1,v);
    }
    else
    {
        if(root->V)
            complete=0; //表示根节点不为空
        root->V=v; //根节点为空的时候就赋值。相当于创建新的结点
    }
}
tree *Q[300];
void output(tree *root) //层次遍历二叉树
{
    int _move=0,save=0;
    Q[_move++]=root; //先将根节点放进去
    cout <<root->V<<endl; //将根结点输出,然后将其左、右子节点放进数组
    while(_move<save)//一层的结点个数恰好等于输出的左右子节点的个数,即一层输完
    {
        tree *now=Q[_move++];
        if(now->L) //左子结点不为空
        {
            cout <<' '<<now->L->V<<endl; //输出左子结点的的值
            Q[save++]=now->L;//并将其放入数组中
        }
        if(now->R)
        {
            cout <<' '<<now->R->V<<endl;
            Q[save++]=now->R;
        }
    }
    cout <<endl; //输完二叉树的一层换行

}
int main()
{
    char buf[256],leaf[256];
    int value;
    complete=1; //初始化
    root=&node[tree_size=0];
    while(~scanf("%s",buf))
    {
        if(!strcmp(buf,"()"))
        {
            if(!complete||!test(root))
            {
                cout <<"no complete."<<endl;
            }
            else
            {
                output(root);
            }
            memset(node ,0,sizeof(node));
            complete=1;
            root=&node[tree_size=0];
        }
        else
        {
            sscanf(buf,"(%d,%s",&value,leaf); //强大的sscanf(),详细见小结
            madetree(root,leaf,value);
        }
    }
    return 0;
}



小结:本题十分重要,也是挺难得,这个题是我看着书写都感觉挺费劲的,要经常回来看这个题。

sscanf()函数的应用:

例子: 
  1. 常见用法。 
  char buf[512] = ; 
  sscanf("123456 ", "%s", buf); 
  printf("%s\n", buf); 
  结果为:123456 
  2. 取指定长度的字符串。如在下例中,取最大长度为4字节的字符串。 
  sscanf("123456 ", "%4s", buf); 
  printf("%s\n", buf); 
  结果为:1234 
  3. 取到指定字符为止的字符串。如在下例中,取遇到空格为止字符串。 
  sscanf("123456 abcdedf", "%[^ ]", buf); 
  printf("%s\n", buf); 
  结果为:123456 
  4. 取仅包含指定字符集的字符串。如在下例中,取仅包含1到9和小写字母的字符串。 
  sscanf("123456abcdedfBCDEF", "%[1-9a-z]", buf); 
  printf("%s\n", buf); 
  结果为:123456abcdedf 
  5. 取到指定字符集为止的字符串。如在下例中,取遇到大写字母为止的字符串。 
  sscanf("123456abcdedfBCDEF", "%[^A-Z]", buf); 
  printf("%s\n", buf); 
  结果为:123456abcdedf 
  6、给定一个字符串iios/12DDWDFF@122,获取 / 和 @ 之间的字符串,先将 "iios/"过滤掉,再将非'@'的一串内容送到buf中 
  sscanf("iios/12DDWDFF@122", "%*[^/]/%[^@]", buf); 
  printf("%s\n", buf); 
  结果为:12DDWDFF 
  7、给定一个字符串““hello, world”,仅保留world。(注意:“,”之后有一空格) 
  sscanf(“hello, world”, "%*s%s", buf); 
  printf("%s\n", buf); 
  结果为:world 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值