问题 N: 二叉树的创建和文本显示
题目描述
编一个程序,读入先序遍历字符串,根据此字符串建立一棵二叉树(以指针方式存储)。
例如如下的先序遍历字符串:
A ST C # # D 10 # G # # F # # #
各结点数据(长度不超过3),用空格分开,其中“#”代表空树。
建立起此二叉树以后,再按要求输出二叉树。
输入
输入由多组测试数据组成。
每组数据包含一行字符串,即二叉树的先序遍历,字符串长度大于0且不超过100。
输出
对于每组数据,显示对应的二叉树,然后再输出一空行。输出形式相当于常规树形左旋90度。见样例。 注意二叉树的每一层缩进为4,每一行行尾没有空格符号。
样例输入 Copy
A ST C # # D 10 # G # # F # # #
4 2 1 # # 3 # # 5 # 6 # #
样例输出 Copy
A
F
D
G
10
ST
C
6
5
4
3
2
1
实现过程
题目内容:
编写一个程序,该程序能够读取一个表示二叉树先序遍历的字符串,并据此创建一棵二叉树。二叉树的节点存储数据,并且每个节点都有指向左子树和右子树的指针。如果先序遍历字符串中的某个位置是空树,使用特殊字符“#”表示。
例如,给定以下先序遍历字符串:
A ST C # # D 10 # G # # F # #
该字符串表示的二叉树应该被如下创建:
- 根节点是“A”,其左子树是“ST”,右子树是“C”。
- “ST”的左右子树都不存在(用“#”表示)。
- “C”的左子树是“D”,右子树是“10”。
- “D”和“10”的左右子树都不存在。
- “C”的右子树是“G”,但它的左右子树不存在。
- “A”的右子树是“F”,但它的左右子树也都不存在。
题目目的:
-
理解二叉树的结构:掌握二叉树的基本概念,包括节点、先序遍历、左子树和右子树。
-
学习先序遍历的特点:在先序遍历中,第一个节点是根节点,紧接着是左子树的遍历,最后是右子树的遍历。
-
实现字符串到二叉树的转换:通过解析先序遍历字符串,学会如何构建相应的二叉树数据结构。
-
掌握递归思想:在构建二叉树和遍历输出二叉树时,使用递归方法解决问题。
-
提高编程能力:通过实际编写代码,提高编程技巧,特别是在处理指针和动态数据结构时。
-
锻炼算法实现能力:实现从字符串到树结构的转换,需要设计和实现有效的算法。
-
增强空间想象能力:通过文本形式输出二叉树,增强对树结构空间布局的理解。
-
学习数据结构的应用:了解如何将理论知识应用于实际问题,例如使用二叉树解决实际问题。
样例输出格式:
程序应该能够根据输入的先序遍历字符串,以文本形式输出对应的二叉树结构。输出格式应类似于:
A
/ \
ST C
/ \
D 10
/
G
/
F
每一层的缩进表示树的层次结构,每个节点后没有空格,且每个节点正确地与其子节点对齐。
注意事项:
- 输入字符串中的节点数据长度不超过3,且节点数据由字母和数字组成。
- 输入字符串中的“#”代表空树,即没有子节点。
- 输出格式要符合题目要求,特别是缩进和对齐。
实现一个简单的文本界面下的二叉树创建和遍历程序。
下面是对代码的详细解析:
-
头文件和命名空间:
- 包含
<bits/stdc++.h>
头文件,它包含了标准库中的大部分内容。 - 使用
using namespace std;
来避免在标准库类型和函数前加std::
。
- 包含
-
全局变量:
depth
用于记录当前的层级深度。judge
用于判断输入是否结束,并跳出循环。
-
结构体定义:
tr
结构体定义了二叉树的节点,包含一个string s
存储节点数据,以及两个指向左右子节点的指针LC
和RC
。
-
递归创建节点:
Creattr
函数用于递归创建二叉树的节点。- 读取节点数据,如果输入的是
#
表示结束,否则递归创建左子树和右子树。
-
递归创建树:
CreatTree
函数用于递归创建整个二叉树。- 读取根节点数据,如果读取失败(文件结束或空字符串),设置
judge
为 1 并返回 0 表示结束。
-
递归输出节点:
Outtr
函数用于递归输出二叉树的节点。- 首先递归输出右子树,然后输出当前节点(带缩进表示层级),最后递归输出左子树。
-
输出树:
OutTree
函数调用Outtr
函数输出二叉树。
-
主函数
main
:- 使用
while(1)
创建无限循环,直到judge
被设置为 1 时跳出。 - 创建
Tree
指针,调用CreatTree
函数创建二叉树。 - 如果
judge
为 1,使用break
跳出循环。 - 调用
OutTree
函数输出二叉树,并输出一个空行。
- 使用
-
程序结束:
- 返回 0,表示程序正常结束。
代码逻辑分析:
- 这段代码使用递归方法来创建和遍历二叉树。
- 使用
#
作为输入结束的标志,允许用户输入空格跳过节点的创建。
潜在问题:
- 使用
cin
直接读取字符串可能不是最佳选择,特别是在处理空格和特殊字符时。 - 代码没有释放动态分配的内存,可能导致内存泄漏。
改进建议:
- 考虑使用
getline(cin, m->s)
来读取包含空格的字符串。 - 在程序结束时,添加代码来释放所有动态分配的内存。
- 可以考虑使用异常处理来管理输入错误和程序错误。
- 为了提高代码的健壮性,可以添加对输入有效性的检查。
部分实现
存储节点数据
typedef struct tr{
string s;
struct tr *LC;
struct tr *RC;
}tr,*Tr;
2.递归创建二叉树的节点
void Creattr(Tr &m){//建立节点
m=new tr;
cin>>m->s;
if(m->s[0]=='#');
else {
Creattr(m->LC);
Creattr(m->RC);}
}
递归创建整个二叉树
int CreatTree(Tr &Tr1){//建立树
Tr1=new tr;
cin>>Tr1->s;
if(!(Tr1->s[0])){judge=1;return 0; }
Creattr(Tr1->LC);
Creattr(Tr1->RC);
return 1;
}
递归输出二叉树的节点
void Outtr(Tr n){//输出节点
depth++;
if(n->s[0]=='#');
else {
Outtr(n->RC);
for(int i=0;i<depth;i++)printf(" ");
cout<<n->s<<endl;
Outtr(n->LC);}
depth--;
}
调用
Outtr
函数输出二叉树
void OutTree(Tr Tr2){//输出树
Outtr(Tr2->RC);
cout<<Tr2->s<<endl;
Outtr(Tr2->LC);
}
AC代码
#include<bits/stdc++.h>
using namespace std;
int depth=0;int judge=0;
//depth记录当前层数,判断空格的输入,judge判断输入是否结束,跳出循环
typedef struct tr{
string s;
struct tr *LC;
struct tr *RC;
}tr,*Tr;
void Creattr(Tr &m){//建立节点
m=new tr;
cin>>m->s;
if(m->s[0]=='#');
else {
Creattr(m->LC);
Creattr(m->RC);}
}
int CreatTree(Tr &Tr1){//建立树
Tr1=new tr;
cin>>Tr1->s;
if(!(Tr1->s[0])){judge=1;return 0; }
Creattr(Tr1->LC);
Creattr(Tr1->RC);
return 1;
}
void Outtr(Tr n){//输出节点
depth++;
if(n->s[0]=='#');
else {
Outtr(n->RC);
for(int i=0;i<depth;i++)printf(" ");
cout<<n->s<<endl;
Outtr(n->LC);}
depth--;
}
void OutTree(Tr Tr2){//输出树
Outtr(Tr2->RC);
cout<<Tr2->s<<endl;
Outtr(Tr2->LC);
}
int main(){
while(1){
Tr Tree;
CreatTree(Tree);
if(judge==1)break;
OutTree(Tree);
cout<<endl;
}
return 0;
}