【代码超详解】UVA 122 Trees on the Level(二叉树、BFS)

本文详细解析了UVA 122 Trees on the Level的问题,涉及二叉树构建、BFS遍历等概念。通过分析算法和编写代码,展示如何从输入的有序对构建二叉树,并按层次顺序输出节点值。文章提供了一种避免大整数处理和节省内存的动态节点添加方法,同时包括了完整的AC代码。
摘要由CSDN通过智能技术生成

紫书 第六章 例题 6-7 Trees on the level, Duke 1993, UVa 122
第一回手写二叉树,今天就为这道题磕了一整天才过了样例,不过好在一提交就AC了。

一、题目描述
输入包含多组数据。每组数据给出一系列有序对 (v, s) (单个空格隔开两个对)代表二叉树的某个节点的值,你的任务是从根到叶输出各个节点的值 v 。同一层输出顺序自然是从左到右。最多有 256 个节点。v 代表某个节点的值,s 是一个仅包含 L 和 R 的字符串,用于描述从树根出发到达该节点的路径。如果有节点未赋值,或者赋值超过 1 次,输出 “not complete” 。(去掉引号)
样例 1 :
(11,LL) (7,LLL) (8,R)
(5,) (4,L) (13,RL) (2,LLR) (1,RRR) (4,RR) ()
输出:
5 4 8 11 13 4 7 2 1
对应的二叉树如图:

样例 2 :
(3,L) (4,R) ()
输出:
not complete
注:因为根未赋值,因此判断该二叉树未完成。

二、算法分析说明与代码编写指导
已知最多有 256 个节点。如果直接按照重要结论 “对于一个结点 k ,其左子结点、右子结点的编号分别是 2k 和 2k + 1 ” 将各个节点编号并储存值,可能会使部分节点的编号异常巨大。举例:如果从根节点开始,每个节点都连上一个节点的左子树,那么第 256 个节点的编号是 2^255 。为了避免手写大整数类、重定义需要的常用运算符,也为了避免大整数类降低运算速率,更为了避免占用较大内存,我们采用动态增加节点的方法。

程序稍显复杂,因此我们采用先写主程序(或伪代码)后写函数的办法。需要的二叉树相关函数先在 main 函数里写上名称,然后再在 main 函数之前将函数的代码补充完整。
主程序的编写思路是:清空上次的输入、树及相关变量→保存输入→依次读取保存的字串并处理→输出。

在本代码中,字符串 c 代表输入的字符。已知节点最多有 256 个,于是描述节点路径的字符串最长达到 256 个。题目未给出值的范围,默认为 32 位整数。经检验可以判定 AC 。本代码采用 unsigned int 记录每个节点的值,最长占 10 个字符。再加上
左右括号和逗号共 3 个字符,一共 269 个字符。描述 256 个节点的有序对共用 255 个空格隔开,并加上结束符 \0 ,一共需要 69120 个字节。读取到描述路径的字串时,用一个 257 字节的字符数组 p 保存。如果比赛期间遇到类似的题目,可以跳过这样的计算,直接在简单估算之后开一个较大的字符数组。
变量 l 代表输入的长度,lp 代表路径的长度,v 代表当前读入的节点的值,并用向量 outseq 记录输出的节点的指针,bool 变量 notcomplete 作为标记,代表是否需要输出 not complete 。

处理要按照读取到的字符分类处理。依据分类讨论思想可以用 switch 语句写出处理的代码。处理输入的算法是:
读取到左括号时,将节点的值 v 记为零,做好读入新值的准备。
读取到数字时,将值存入 v 。读到当前位时,v 应该改为 10 * v + c[i] - 48 。48 是数字 0 的 ASCII 码。如果不减去 48 ,会读取到错误的数值。表达式证明略。
读取到逗号时,判断右侧是否紧跟右括号。若是,刚才读到的值 v 为根节点的值。若否,先清空上次记录的路径,为接下来读取描述路径的字符串做准备。
读取到 L 或 R 时,记录路径。
读取到右括号时,判断左侧字符。如果为逗号,则此时正在读入根节点,不作处理,继续读取下一个字符。如果为左括号,意味着这棵二叉树输入结束。如果都不是,则按照刚才读取到的路径调用添加节点函数 AddNode 。执行完毕后,如果 notcomplete 标记被改为 true ,直接放弃剩余输入跳出循环,输出 not complete 。
读取到空格时,忽略。

接下来写二叉树相关的函数。首先定义一个结构体 Node。依据题目要求,包含一个 bool 值 HasValue 标记是否已经写入过值,value 代表该节点的值,并有左子树指针 left 和右子树指针 right 。用指针实现的手写二叉树一般至少包含三个值:节点值、左子树指针和右子树指针。
接下来编写 AddNode 节点添加函数。根据传回来的路径,从树根开始处理。依据读到的字符是 L 还是 R 判断修改左子树还是右子树。如果左子树为空指针(无子树),需要调用 NewNode 函数来申请一个新指针(当然这个过程也创建了一个新的 Node 节点)。对右子树同理。处理完毕后,将当前指针改为子树指针。
依据路径构造完新的节点(树叶)后,记得将 HasValue 标记改为 True,并写入值 value 。如果已经有值,将 notcomplete 标记改为 true 。
接下来编写 NewNode 函数。直接返回一个新指针即可。
接下来编写 RemoveTree 函数,用于删除上次输入构造的树。从根节点开始,采用递归一并删除子节点。如果子节点为 NULL ,直接停止执行函数。全部搜寻到树叶后,子节点本身就会在执行 delete 函数时层层删除,直到删除根节点。

如果输入没有问题,则输出二叉树各节点的值。接下来编写输出的函数。这是一种 BFS (广度优先搜索)算法。在本题中,输出的算法是:
先往输出序列中存入树根。然后执行循环,退出循环的条件是输出序列的最后一个节点的值输出完毕或有节点未写入数值。循环主体为:
如果放入的某个节点未写入值(能执行到输出环节意味着已经通过之前的代码验证没有节点被多次赋值,此处不用再考虑),退出循环,输出 not complete 。
如果放入的某个节点存在子树,将子树添加到输出序列。
循环执行完毕,即验证没有未赋值的节点。此时可以按顺序输出整个序列中各节点的值。
queue 的 push / emplace 动作比 vector 的 push_back / emplace_back 慢,本输出算法避免了紫书上提供的输出算法中的队列操作,提升了性能。
一定不要忘记,每次输入新树之前,要清除原有输入和已经构造的树,清除输出序列,清除变量。

三、AC 代码(0 ms)
本代码根据紫书上的代码优化而来。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#pragma warning(disable:4996)
using namespace std;
struct Node {
   
	bool HasValue; unsigned int value; Node* left, * right; 
	Node() :HasValue(false), value(0), left(NULL), right(NULL) {
   }
};
Node* root; char c[</
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值