文章目录
写在开头
《数据结构(C语言版)》 (严蔚敏等著)一书在描述算法时使用的是类C语言。这种语言是介于伪码语言和程序设计语言之间的一种表示形式,它保留了C语言的精华,同时也添加了一些C++的成分。这种语言不拘泥与C语言的语法细节,便于理解、阅读,也能方便的转换成C语言,便于简明扼要的描述算法,突出算法的思路。
然而,用类C语言语言来描述算法的形式使算法显得比较零散,无法体现出系统的整体性(即将各个基本运算的功能组织在一个系统中的方法),会使初学C语言的同学难以深入理解数据结构中逻辑结构与物理结构的关系。
在完成数据结构课程设计“基于二叉链表的二叉树实现”时,我尝试以纯C语言的形式实现基于二叉链表的二叉树。
课程推荐
华中科技大学《数据结构》慕课(李国徽、袁凌、祝建华、许贵平、周时阳)
文章推荐
二叉树的基本概念
二叉树是一种树型结构,即 n n n个结点的有限集。它的特点是每个结点至多只有两棵子树(即二叉树中不存在度大于2的结点)。此外,二叉树的子树有左右之分,其次序不能任意颠倒。
- 根节点:一棵树最上面的节点称为根节点。
- 父节点、子节点:如果一个节点下面连接多个节点,那么该节点称为其下面节点的父亲节点,它下面的节点称为其孩子节点。
- 叶子节点:没有任何子节点的节点称为叶子节点。
- 兄弟节点:具有相同父节点的节点互称为兄弟节点。
抽象数据类型二叉树的定义
A
D
T
B
i
n
a
r
y
T
r
e
e
{
ADT\space BinaryTree\space \{
ADT BinaryTree {
数
据
对
象
D
:
D
是
具
有
相
同
特
性
的
数
据
元
素
的
集
合
。
数据对象D:D是具有相同特性的数据元素的集合。
数据对象D:D是具有相同特性的数据元素的集合。
数
据
关
系
R
:
数据关系R:
数据关系R:
若
D
=
∅
,
则
R
=
∅
,
称
B
i
n
a
r
y
T
r
e
e
为
空
二
叉
树
;
\space \space 若D=\varnothing,则R=\varnothing,称BinaryTree为空二叉树;
若D=∅,则R=∅,称BinaryTree为空二叉树;
若
D
≠
∅
,
则
R
=
{
H
}
,
H
是
如
下
二
元
关
系
:
\space \space 若D\ne \varnothing,则R=\{H\},H是如下二元关系:
若D=∅,则R={H},H是如下二元关系:
(
1
)
在
D
中
存
在
唯
一
的
称
为
根
的
数
据
元
素
r
o
o
t
,
它
在
关
系
H
中
无
前
驱
;
\space \space \space \space (1)在D中存在唯一的称为根的数据元素root,它在关系H中无前驱;
(1)在D中存在唯一的称为根的数据元素root,它在关系H中无前驱;
(
2
)
若
D
−
{
r
o
o
t
}
≠
∅
,
则
存
在
D
−
{
r
o
o
t
}
=
{
D
l
,
D
r
}
,
且
D
l
∩
D
r
=
∅
;
\space \space \space \space (2)若D-\{root\}\ne\varnothing,则存在D-\{root\}=\{D_l,D_r\},且D_l \cap D_r=\varnothing;
(2)若D−{root}=∅,则存在D−{root}={Dl,Dr},且Dl∩Dr=∅;
(
3
)
若
D
l
≠
∅
,
则
D
l
中
存
在
唯
一
的
元
素
x
l
,
<
r
o
o
t
,
x
l
>
∈
H
,
且
存
在
D
l
上
的
关
系
H
l
⊂
H
;
\space \space \space \space (3)若D_l \ne \varnothing ,则D_l中存在唯一的元素x_l,<root,x_l>∈H,且存在D_l上的关系H_l\subset H;
(3)若Dl=∅,则Dl中存在唯一的元素xl,<root,xl>∈H,且存在Dl上的关系Hl⊂H;
若
D
r
≠
∅
,
则
D
r
中
存
在
唯
一
的
元
素
x
r
,
<
r
o
o
t
,
x
r
>
∈
H
,
且
存
在
D
r
上
的
关
系
H
r
⊂
H
;
\space \space \space \space \space \space \space \space \space \space \space \space \space \space 若D_r \ne \varnothing,则D_r中存在唯一的元素x_r,<root,x_r> \in H,且存在D_r上的关系H_r \subset H;
若Dr=∅,则Dr中存在唯一的元素xr,<root,xr>∈H,且存在Dr上的关系Hr⊂H;
H
=
{
<
r
o
o
t
,
x
l
>
,
<
r
o
o
t
,
x
r
>
,
H
l
,
H
r
}
\space \space \space \space \space \space \space \space \space \space \space \space \space \space H= \{ <root,x_l>,<root,x_r>,H_l,H_r \}
H={<root,xl>,<root,xr>,Hl,Hr}
(
4
)
(
D
l
,
{
H
l
}
)
是
一
棵
符
合
本
定
义
的
二
叉
树
,
称
为
根
的
左
子
树
;
\space \space \space \space (4)(D_l,\{ H_l \})是一棵符合本定义的二叉树,称为根的左子树;
(4)(Dl,{Hl})是一棵符合本定义的二叉树,称为根的左子树;
(
D
r
,
{
H
r
}
)
也
是
一
棵
符
合
本
定
义
的
二
叉
树
,
称
为
根
的
右
子
树
。
\space \space \space \space \space \space \space \space \space \space \space \space \space \space (D_r,\{ H_r \})也是一棵符合本定义的二叉树,称为根的右子树。
(Dr,{Hr})也是一棵符合本定义的二叉树,称为根的右子树。
基
本
操
作
P
:
基本操作P:
基本操作P:二叉树基本操作定义
}
\}
}
课程设计任务描述
实验任务
采用二叉链表作为二叉树的物理结构,构造一个具有菜单的功能演示系统,实现二叉树的基本运算。其中,在主函数中完成函数调用所需实参值的准备和函数执行结果的显示,并给出适当的操作提示。程序还应该定义初始化二叉树、销毁二叉树、创建二叉树、清空二叉树、判定空二叉树和求二叉树深度等基本运算对应的函数。在此基础上,可以选择以文件的形式高效保存二叉树数据逻辑结构 ( D , { R } ) (D,\{ R \} ) (D,{R})的完整信息,实现对二叉树的存储和加载,即既可以将生成的二叉树存入到相应的文件中,也可以从文件中获取二叉树进行操作。同时,演示系统还可实现多个二叉树的管理。
二叉树基本操作定义
依据最小完备性和常用性相结合的原则,以函数形式定义了二叉树的初始化二叉树、销毁二叉树、创建二叉树、清空二叉树、判定空二叉树和求二叉树深度等20种基本运算,具体运算功能定义如下:
- 初始化二叉树
InitBiTree(&T)
:初始条件是二叉树T不存在;操作结果是构造空二叉树。 - 销毁二叉树
DestroyBiTree(&T)
:初始条件是二叉树T已存在;操作结果是销毁二叉树T。 - 创建二叉树
CreateBiTree(&T,definition)
:初始条件是definition 给出二叉树T的定义;操作结果是按definition构造二叉树T。 - 清空二叉树
ClearBiTree (&T)
:初始条件是二叉树T存在; 操作结果是将二叉树T清空。 - 判定空二叉树
BiTreeEmpty(T)
:初始条件是二叉树T存在;操作结果是若T为空二叉树,则返回TRUE,否则,返回FALSE。 - 求二叉树深度
BiTreeDepth(T)
:初始条件是二叉树T存在;操作结果是返回T的深度。 - 获得根结点
Root(T)
:初始条件是二叉树T已存在;操作结果是返回二叉树T的根节点。 - 获得结点
Value(T,e)
:初始条件是二叉树T已存在,e是T中的某个结点;操作结果是返回节点e的值。 - 结点赋值
Assign(T,&e,value)
:初始条件是二叉树T已存在,e是T中的某个结点;操作结果是结点e赋值为value。 - 获得双亲结点
Parent(T,e)
:初始条件是二叉树T已存在,e是T中的某个结点。操作结果是若e是T的非根结点,则返回它的双亲结点指针;否则返回NULL。 - 获得左孩子结点
LeftChild(T,e)
:初始条件是二叉树T存在,e是T中某个节点。操作结果是返回e的左孩子结点;若e无左孩子,则返回NULL。 - 获得右孩子结点
RightChild(T,e)
:初始条件是二叉树T已存在,e是T中某个结点。操作结果是返回e的右孩子结点;若e无右孩子,则返回NULL。 - 获得左兄弟结点
LeftSibling(T,e)
:初始条件是二叉树T存在。e是T中某个结点;操作结果是返回e的左兄弟结点指针;若e是T的左孩子或者无左兄弟,则返回NULL。 - 获得右兄弟结点
RightSibling(T,e)
:初始条件是二叉树T已存在。e是T中某个结点;操作结果是返回e的右兄弟结点指针;若e是T的右孩子或者无有兄弟,则返回NULL。 - 插入子树
InsertChild(T,p,LR,c)
:初始条件是二叉树T存在,p指向T中的某个结点,LR为0或1,非空二叉树c与T不相交且右子树为空;操作结果是根据LR为0或者1,插入c为T中p所指结点的左或右子树,p所指结点的原有左子树或右子树则为c的右子树。 - 删除子树
DeleteChild(T,p,LR)
:初始条件是二叉树T存在,p指向T中的某个结点,LR为0或1;操作结果是根据LR为0或者1,删除c为T中p所指结点的左或右子树。 - 前序遍历
PreOrderTraverse(T,Visit())
:初始条件是二叉树T存在,Visit是对结点操作的应用函数;操作结果是先序遍历T,对每个结点调用函数Visit一次且一次,一旦调用失败,则操作失败。 - 中序遍历
InOrderTraverse(T,Visit())
:初始条件是二叉树T存在,Visit是对结点操作的应用函数;操作结果是中序遍历T,对每个结点调用函数Visit一次且一次,一旦调用失败,则操作失败。 - 后序遍历
PostOrderTraverse(T,Visit())
:初始条件是二叉树T存在,Visit是对结点操作的应用函数;操作结果是后序遍历T,对每个结点调用函数Visit一次且一次,一旦调用失败,则操作失败。 - 层序遍历
LevelOrderTraverse(T,Visit())
:初始条件是二叉树T存在,Visit是对结点操作的应用函数;操作结果是层序遍历T,对每个结点调用函数Visit一次且一次,一旦调用失败,则操作失败。
基于二叉链表的二叉树的实现
状态返回值类型和数据元素类型的定义
typedef int status;
typedef int ElemType;
二叉链表节点类型的定义
系统采用二叉链表结构存储二叉树,二叉链表节点含数据域、左孩子节点指针、右孩子节点指针三部分。
typedef struct Node
{
struct Node *LeftChild;
ElemType data;
struct Node *RightChild;
}Node,*BiTree;
多二叉树的头指针构成的链表节点类型的定义
typedef struct BiTreeHeadPointer
{
BiTree head;
struct BiTreeHeadPointer *next;
}BiTreeHeadPointer,*BiTreeList;
“二叉树不存在”与“空二叉树”的定义
由于在二叉树基本运算操作的初始条件中对“二叉树不存在”和“空二叉树”做了区分。为了区分“二叉树不存在”和“空二叉树”,我们可能就要引入“带表头节点的二叉链表”(暂且称这个节点为“二叉链表的头节点”或“树头节点”)。
然而,在将基本运算操作转化成C语言的函数时,大多数复杂的操作是通过循环和递归实现的。“树头节点”的出现使得递归的实现变得更加复杂。
所以,我们不妨舍弃这种做法,做如下定义:
- T==NULL时,该二叉树不存在。
- T!=NULL&&T->data==0x7fffffff(无穷大)时,该二叉树是空二叉树。
宏定义
此处用编译预处理(宏定义)的方法,定义了TRUE、FALSE、OK、ERROR、OVERFLOW等六个值,表示真、假、成功、失败、溢出等状态。此外,还定义MAX_SIZE、Set_MAXSIZE、FILENAME_LENGTH,分别用于表示链表头指针数组的最大长度、层序遍历临时数组的最大长度以及文件路径的最大长度。
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR -1
#define OVERFLOW -2
#define MAX_SIZE 20
#define Set_MAXSIZE 50
#define FILENAME_LENGTH 30
节点访问函数
- visit函数用于访问节点,输出该节点数据域的值。
关于visit函数
status visit (ElemType e)
{
printf(" %d ",e);
return OK;
}
同时管理多个二叉树的实现
- 演示系统1采用顺序存储的线性表(数组)的方式管理多个二叉树,可实现多个二叉树的管理。
- 演示系统2采用链式存储的线性表(单链表)的方式管理多个二叉树,可实现多个二叉树的管理。
算法实现例讲——层序遍历算法
使用队列实现层序遍历算法
这种方法比较常见,但是,使用C语言来实现使用队列并不简单,所以这里就不赘述了。对该算法有兴趣的可以参考:
使用数组模拟队列实现层序遍历算法
这里,我们需要建立起层序遍历节点指针数组,用于存储层序遍历的每个结点的地址。游走监视哨i从1开始,指向数组的下一个空位,j指向正在遍历的节点的父亲节点。将根节点放入数组的第一个位置。利用循环,若Queue[ j ]不为空,访问Queue[ j ],此后将Queue[ j ]的左孩子节点和右孩子节点的地址依次放入数组(i不断向后移动)。此后,j自增。直至i<=j。返回成功。
status LevelOrderTraverse(BiTree T,status (*visit)(ElemType e))
{
Node *Queue[Set_MAXSIZE]={NULL}; //节点数组:存储层序遍历的每个结点指针
Node *LeftChild=NULL,*RightChild=NULL;
//游走监视哨:i指向数组的下一个空位,j指向正在遍历的节点的父亲节点
int i=1,j=0;
Queue[0]=T; //首先遍历根节点
while(i>j) //循环结束条件:j>=i
{
if (Queue[j]) //若Queue[j]!=NULL,访问其左右孩子节点
{
visit(Queue[j]->data); //访问j指向的节点
LeftChild=Queue[j]->LeftChild;
RightChild=Queue[j]->RightChild;
if(LeftChild) Queue[i++]=LeftChild; //存储j的非空左孩子结点,i++
if(RightChild) Queue[i++]=RightChild; //存储j的非空右孩子结点,i++
}
j++; //j指向上一层的下一个节点
}
return OK;
}
使用类似递归的方式实现层序遍历算法
此算法的核心是对递归、参数的传递与概念的理解。不难发现,节点在第i层的实际含义是从根节点到该节点的路径长度为i。
此算法参考了二叉树的层序遍历(递归与非递归),同时依据演示系统的其他部分对算法进行了改动,优化了LevelTraverse函数。
//按层遍历:初始条件是二叉树T存在;操作结果是层序遍历T,对每个结点调用函数visit一次。
status LevelOrderTraverse(BiTree T,status (*visit)(ElemType e))
{
int Depth=BiTreeDepth(T),Level_Destination; //获取二叉树的深度
for(Level_Destination=1;Level_Destination<=Depth;Level_Destination++)
LevelTraverse(T,1,Level_Destination,visit); //依次输出每一层
return OK;
}
//遍历输出二叉树的某一层:初始条件是二叉树T存在;操作结果是遍历输出二叉树的第i层。
status LevelTraverse(BiTree T,int Level_Now,int Level_Destination,status (*visit)(ElemType e))
{
if(T==NULL) return ERROR; //ERROR:该节点为空节点,返回
//若该节点在目标层,输出该节点
if(Level_Now==Level_Destination) visit(T->data);
else
{
//若该节点不在目标层,依次检查其左、右孩子节点是否在目标层(保证层序从左到右)
LevelTraverse(T->LeftChild,Level_Now+1,Level_Destination,visit);
LevelTraverse(T->RightChild,Level_Now+1,Level_Destination,visit);
}
return OK;
}
演示系统菜单功能示意图
![](https://i-blog.csdnimg.cn/blog_migrate/2e1eb325bde5b3bca79fc5c29d4c4cd6.png)
集成开发环境(IDE)
以下代码在Microsoft Visual C++ 2010 Express下预编译,编译,汇编,链接通过(有警告warning)。
系统的代码实现
演示系统1(采用顺序存储的线性表管理多个二叉树)
//Binary Tree on Linked Storage Structure
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
//编译预处理
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR -1
#define OVERFLOW -2
#define MAX_SIZE 20
#define Set_MAXSIZE 50
#define FILENAME_LENGTH 30
//定义状态返回值类型和数据元素类型
typedef int status;
typedef int ElemType;
//定义二叉链表节点类型
typedef struct Node
{
struct Node *LeftChild;
ElemType data;
struct Node *RightChild;
}Node,*BiTree;
//函数声明
status InitBiTree(BiTree *T,BiTree BiTreeSet[]);
status DestroyBiTree(BiTree *T,BiTree BiTreeSet[]);
status CreateBiTree(BiTree *T);
status ClearBiTree(BiTree *T,BiTree BiTreeSet[]);
status BiTreeEmpty(BiTree T);
int BiTreeDepth(BiTree T);
Node *Root(BiTree T);
ElemType Value(BiTree T,Node *e);
status Assign(BiTree T,Node *e,ElemType value);
Node *Parent(BiTree T,Node *e);
Node *LeftChild(BiTree T,Node *e);
Node *RightChild(BiTree T,Node *e);
Node *LeftSibling(BiTree T,Node *e);
Node *RightSibling(BiTree T,Node *e);
status InsertChild(BiTree T,Node *p,int LR,BiTree c);
status DeleteChild(BiTree T,Node *p,int LR,BiTree BiTreeSet[]);
status PreOrderTraverse(BiTree T,status (*visit)(ElemType e));
status InOrderTraverse(BiTree T,status (*visit)(ElemType e));
status PostOrderTraverse(BiTree T,status (*visit)(ElemType e));
status LevelOrderTraverse(BiTree T,status (*visit)(ElemType e));
//status LevelTraverse(BiTree T,int Level_Now,int Level_Destination,status (*visit)(ElemType e))
status GetNode(BiTree T,int num,Node **node);
status SaveBiTree(BiTree T);
status WriteNodeToFile(BiTree T,FILE *fp);
status LoadBiTree(BiTree *T,BiTree BiTreeSet[]);
status ReadNodeFromFile(BiTree *T,FILE *fp);
status CreatAnotherBiTree(BiTree *T,BiTree BiTreeSet[]);
status ChooseBiTree(BiTree *T,BiTree BiTreeSet[]);
status visit (ElemType e)
{
printf(" %d ",e);
return OK;
}
int main(void)
{
int op=1,state; //op=1:程序入口,state:接受返回值,表示函数返回状态
int i,e,LR; //LR:左子树(LR=0),右子树(LR=1)
BiTree T=NULL; //T:二叉树名、头指针
BiTree BiTreeSet[MAX_SIZE]={NULL}; //BiTreeSet:存储二叉树的头指针
Node *Temp=NULL,*NewTree=NULL;
while(op)
{
system("cls");
printf("\n\n");
printf(" Menu for Binary Tree on Linked Storage Structure \n");
printf("------------------------------------------------------------\n");
printf(" 0. Exit \n");
printf(" 1. InitBiTree 2. DestroyBiTree \n");
printf(" 3. CreateBiTree 4. ClearBiTree \n");
printf(" 5. BiTreeEmpty 6. BiTreeDepth \n");
printf(" 7. BiTreeRoot 8. GetNodeValue \n");
printf(" 9. AssignNode 10. GetNodeParent \n");
printf(" 11. GetLeftChild 12. GetRightChild \n");
printf(" 13. GetLeftSibling 14. GetRightSibling \n");
printf(" 15. InsertChild 16. DeleteChild \n");
printf(" 17. PreOrderTraverse 18. InOrderTraverse \n");
printf(" 19. PostOrderTraverse 20. LevelOrderTraverse \n");
printf(" 21. SaveBiTree 22. LoadBiTree \n");
printf(" 23. CreatAnothBiTree 24. ChooseBiTree \n");
printf("------------------------------------------------------------\n");
printf(" 请选择你的操作[0~24]:");
//若输入数字之外的字符,清空标准输入流里的数据,并将op置为default
if(scanf("%d",&op)!=1) {fflush(stdin);op=25;}
switch(op)
{
case 1:
state=InitBiTree(&T,BiTreeSet);
if(state==OK) printf("二叉树初始化成功!\n");
else printf("二叉树已存在!\n");
system("pause");
break;
case 2:
if(T==NULL) printf("二叉树不存在,无需销毁!\n");
else
{
if(DestroyBiTree(&T,BiTreeSet)==OK) printf("二叉树销毁成功!\n");
else printf("二叉树销毁失败!\n");
}
system("pause");
break;
case 3:
if(T==NULL) printf("二叉树不存在!\n");
else if(T->data!=0x7fffffff) printf("非空二叉树已存在!若要对其进行创建操作,请先清空该树!\n");
else
{
printf("请输入先序遍历结果的二叉树,叶子节点的左右孩子节点为0:");
if(CreateBiTree(&T)==OK) printf("创建成功!\n");
else printf("创建失败!\n");
}
system("pause");
break;
case 4:
state=ClearBiTree(&T,BiTreeSet);
if(state==OK) printf("二叉树清空成功!\n");
else printf("二叉树清空失败!\n");
system("pause");
break;
case 5:
state=BiTreeEmpty(T);
if(state==TRUE) printf("该树是空树!\n");
else if(state==FALSE) printf("该树不是空树!\n");
system("pause");
break;
case 6:
if(T==NULL) printf("二叉树不存在!\n");
else if(T->data==0x7fffffff) printf("该二叉树为空二叉树!\n");
else printf("该二叉树的深度为%d!\n",BiTreeDepth(T));
system("pause");
break;
case 7:
if(T==NULL) printf("二叉树不存在!\n");
else if(T->data==0x7fffffff) printf("该二叉树为空二叉树!\n");
else printf("该二叉树根节点的数据元素为%d!\n",Root(T)->data);
system("pause");
break;
case 8:
if(T==NULL) printf("二叉树不存在!\n");
else if(T->data==0x7fffffff) printf("该二叉树为空二叉树!\n");
else
{
printf("输入i,获取二叉树层序遍历下第i个数据元素的值:");
scanf("%d",&i);
if(GetNode(T,i,&Temp)==ERROR) printf("输入错误,不存在第%d个数据元素!\n",i);
else
{
state=Value(T,Temp);
if(state==ERROR) printf("操作失败!\n");
else printf("二叉树层序遍历下第%d个数据元素的值为%d!\n",i,Value(T,Temp));
}
}
system("pause");
break;
case 9:
if(T==NULL) printf("二叉树不存在!\n");
else if(T->data==0x7fffffff) printf("该二叉树为空二叉树!\n");
else
{
printf("输入i,e,将二叉树层序遍历下第i个数据元素赋值为 e :");
scanf("%d%d",&i,&e);
if(GetNode(T,i,&Temp)==ERROR) printf("输入错误,不存在第%d个数据元素!\n",i);
else
{
state=Assign(T,Temp,e);
if(state==ERROR) printf("操作失败!\n");
else printf("赋值成功!\n");
}
}
system("pause");
break;
case 10:
if(T==NULL) printf("二叉树不存在!\n");
else if(T->data==0x7fffffff) printf("该二叉树为空二叉树!\n");
else
{
printf("输入i,获取二叉树层序遍历下第i个数据元素的父亲节点的值 :");
scanf("%d",&i);
if(GetNode(T,i,&Temp)==ERROR) printf("输入错误,不存在第%d个数据元素!\n",i);
else
{
Temp=Parent(T,Temp);
if(Temp==NULL) printf("操作失败!该节点没有父亲节点!\n");
else printf("二叉树层序遍历下第%d个数据元素的父亲节点的值为%d!\n",i,Value(T,Temp));
}
}
system("pause");
break;
case 11:
if(T==NULL) printf("二叉树不存在!\n");
else if(T->data==0x7fffffff) printf("该二叉树为空二叉树!\n");
else
{
printf("输入i,获取二叉树层序遍历下第i个数据元素的左孩子节点的值 :");
scanf("%d",&i);
if(GetNode(T,i,&Temp)==ERROR) printf("输入错误,不存在第%d个数据元素!\n",i);
else
{
Temp=LeftChild(T,Temp);
if(Temp==NULL) printf("操作失败!该节点没有左孩子节点!\n");
else printf("二叉树层序遍历下第%d个数据元素的左孩子节点的值为%d!\n",i,Value(T,Temp));
}
}
system("pause");
break;
case 12:
if(T==NULL) printf("二叉树不存在!\n");
else if(T->data==0x7fffffff) printf("该二叉树为空二叉树!\n");
else
{
printf("输入i,获取二叉树层序遍历下第i个数据元素的右孩子节点的值 :");
scanf("%d",&i);
if(GetNode(T,i,&Temp)==ERROR) printf("输入错误,不存在第%d个数据元素!\n",i);
else
{
Temp=RightChild(T,Temp);
if(Temp==NULL) printf("操作失败!该节点没有右孩子节点!\n");
else printf("二叉树层序遍历下第%d个数据元素的右孩子节点的值为%d!\n",i,Value(T,Temp));
}
}
system("pause");
break;
case 13:
if(T==NULL) printf("二叉树不存在!\n");
else if(T->data==0x7fffffff) printf("该二叉树为空二叉树!\n");
else
{
printf("输入i,获取二叉树层序遍历下第i个数据元素的左兄弟节点的值 :");
scanf("%d",&i);
if(GetNode(T,i,&Temp)==ERROR) printf("输入错误,不存在第%d个数据元素!\n",i);
else
{
Temp=LeftSibling(T,Temp);
if(Temp==NULL) printf("操作失败!该节点没有左兄弟节点!\n");
else printf("二叉树层序遍历下第%d个数据元素的左兄弟节点的值为%d!\n",i,Value(T,Temp));
}
}
system("pause");
break;
case 14:
if(T==NULL) printf("二叉树不存在!\n");
else if(T->data==0x7fffffff) printf("该二叉树为空二叉树!\n");
else
{
printf("输入i,获取二叉树层序遍历下第i个数据元素的右兄弟节点的值 :");
scanf("%d",&i);
if(GetNode(T,i,&Temp)==ERROR) printf("输入错误,不存在第%d个数据元素!\n",i);
else
{
Temp=RightSibling(T,Temp);
if(Temp==NULL) printf("操作失败!该节点没有右兄弟节点!\n");
else printf("二叉树层序遍历下第%d个数据元素的右兄弟节点的值为%d!\n",i,Value(T,Temp));
}
}
system("pause");
break;
case 15:
if(T==NULL) printf("二叉树不存在!\n");
else if(T->data==0x7fffffff) printf("该二叉树为空二叉树!\n");
else
{
printf("输入i,以二叉树层序遍历下第i个节点为根节点,插入新树 :");
scanf("%d",&i);
printf("请选择:\n0.插入到该节点的左子树。\n1.插入到该节点的右子树。\n");
scanf("%d",&LR);
if(GetNode(T,i,&Temp)==ERROR) printf("输入错误,不存在第%d个数据元素!\n",i);
else if(LR!=0&&LR!=1) printf("输入错误!请输入0或1以选择插入到该节点的左子树或右子树。\n");
else
{
//初始化NewTree,将NewTree置为空树
NewTree=(BiTree)malloc(sizeof(Node));
NewTree->data=0x7fffffff;
NewTree->LeftChild=NewTree->RightChild=NULL;
printf("请输入先序遍历结果的二叉树,叶子节点的左右孩子节点为0,同时保证该二叉树右子树为空:\n");
CreateBiTree(&NewTree);
state=InsertChild(T,Temp,LR,NewTree);
if(state==ERROR) printf("操作失败!\n");
else printf("操作成功!\n");
}
}
system("pause");
break;
case 16:
if(T==NULL) printf("二叉树不存在!\n");
else if(T->data==0x7fffffff) printf("该二叉树为空二叉树!\n");
else
{
printf("输入i,删除以二叉树层序遍历下第i个数据元素为根节点的子树:");
scanf("%d",&i);
printf("请选择:\n0.删除以该节点为根节点的左子树。\n1.删除以该节点为根节点的右子树。\n");
scanf("%d",&LR);
if(GetNode(T,i,&Temp)==ERROR) printf("输入错误,不存在第%d个数据元素!\n",i);
else
{
state=DeleteChild(T,Temp,LR,BiTreeSet);
if(state==ERROR) printf("操作失败!\n");
else printf("删除成功!\n");
}
}
system("pause");
break;
case 17:
if(T==NULL) printf("二叉树不存在!\n");
else if(T->data==0x7fffffff) printf("该二叉树为空二叉树!\n");
else
{
if(PreOrderTraverse(T,visit)==OK) printf("\n前序遍历成功!\n");
else printf("\n前序遍历失败!\n");
}
system("pause");
break;
case 18:
if(T==NULL) printf("二叉树不存在!\n");
else if(T->data==0x7fffffff) printf("该二叉树为空二叉树!\n");
else
{
if(InOrderTraverse(T,visit)==OK) printf("\n中序遍历成功!\n");
else printf("\n中序遍历失败!\n");
}
system("pause");
break;
case 19:
if(T==NULL) printf("二叉树不存在!\n");
else if(T->data==0x7fffffff) printf("该二叉树为空二叉树!\n");
else
{
if(PostOrderTraverse(T,visit)==OK) printf("\n后序遍历成功!\n");
else printf("\n后序遍历失败!\n");
}
system("pause");
break;
case 20:
if(T==NULL) printf("二叉树不存在!\n");
else if(T->data==0x7fffffff) printf("该二叉树为空二叉树!\n");
else
{
if(LevelOrderTraverse(T,visit)==OK) printf("\n层序遍历成功!\n");
else printf("\n层序遍历失败!\n");
}
system("pause");
break;
case 21:
if(SaveBiTree(T)==OK) printf("二叉树保存成功!\n");
else printf("二叉树保存失败!\n");
system("pause");
break;
case 22:
if(LoadBiTree(&T,BiTreeSet)==OK) printf("二叉树加载成功!\n");
else printf("二叉树加载失败!\n");
system("pause");
break;
case 23:
if(CreatAnotherBiTree(&T,BiTreeSet)==OK) printf("创建新的二叉树成功!\n");
else printf("操作失败!\n");
system("pause");
break;
case 24:
if(ChooseBiTree(&T,BiTreeSet)==OK) printf("成功选中该二叉树!\n");
else printf("操作失败!\n");
system("pause");
break;
case 0:
break;
default:
printf("输入无效!菜单功能选择失败!\n");
system("pause");
break;
}//end of switch
}//end of while
printf("欢迎下次再使用本系统!\n");
return 0;
}//end of main()
//初始化二叉树:初始条件是二叉树T不存在;操作结果是构造空二叉树T。
//二叉树不存在:T==NULL
//空二叉树:T!=NULL&&T->data==0x7fffffff
status InitBiTree(BiTree *T,BiTree BiTreeSet[])
{
int i;
if((*T)==NULL) //初始条件:二叉树T不存在
{
(*T)=(BiTree)malloc(sizeof(Node)); //为根节点分配空间
(*T)->data=0x7fffffff; //数据域置为0x7fffffff,将该树置为空二叉树
(*T)->LeftChild=(*T)->RightChild=NULL; //左右孩子节点指针置为NULL
for(i=0;i<MAX_SIZE;i++) if(BiTreeSet[i]==NULL) break; //找到二叉树头指针数组第一个为NULL的位置
if(i==MAX_SIZE) exit(OVERFLOW); //ERROR:OVERFLOW
BiTreeSet[i]=(*T); //将该二叉树添加至链表头指针数组
return OK;
}
else return ERROR; //ERROR:二叉树已经存在
}
//销毁二叉树:初始条件是二叉树T已存在;操作结果是销毁二叉树T。
status DestroyBiTree(BiTree *T,BiTree BiTreeSet[])
{
int i;
if((*T)==NULL) return OK; //返回OK:空节点
DestroyBiTree(&(*T)->LeftChild,BiTreeSet); //销毁左子树
DestroyBiTree(&(*T)->RightChild,BiTreeSet); //销毁右子树
for(i=0;i<MAX_SIZE;i++) if(BiTreeSet[i]==*T) break; //在二叉树头指针数组中查找该头指针
if(i<MAX_SIZE) //若该二叉树存在于头指针数组中时,将其删除
{
//在二叉树头指针数组中删除该头指针,后续元素前移
//特殊情况:
//1.数组存储了MAX_SIZE个二叉树;
//2.T为第MAX_SIZE个二叉树
while(i<MAX_SIZE-1&&BiTreeSet[i]!=NULL)
{
BiTreeSet[i]=BiTreeSet[i+1];
i++;
}
BiTreeSet[MAX_SIZE-1]=NULL; //处理BiTreeSet[MAX_SIZE-1]
}
free(*T); //释放根节点
(*T)=NULL; //头指针置空
return OK;
}
//创建二叉树:初始条件是definition 给出二叉树T的定义;操作结果是按definition构造二叉树T。
status CreateBiTree(BiTree *T)
{
ElemType Data;
if(scanf("%d",&Data)!=EOF&&Data!=0) //输入二叉树节点的值(非0)
{
if((*T)==NULL) //若节点为NULL,创建节点
{
(*T)=(BiTree)malloc(sizeof(Node)); //分配空间
(*T)->LeftChild=(*T)->RightChild=NULL; //左右孩子节点指针置为NULL
if((*T)==NULL) exit(OVERFLOW); //ERROR:OVERFLOW
}
(*T)->data=Data; //节点数据域赋值
CreateBiTree(&(*T)->LeftChild); //递归:创建该节点的左子树
CreateBiTree(&(*T)->RightChild); //递归:创建该节点的右子树
return OK;
}
else return ERROR; //ERROR:根节点为0
}
//清空二叉树:初始条件是二叉树T存在;操作结果是将二叉树T清空。
status ClearBiTree(BiTree *T,BiTree BiTreeSet[])
{
if((*T)==NULL) {printf("二叉树不存在,无需清空!\n");return ERROR;}
else if((*T)->data==0x7fffffff) {printf("该二叉树为空二叉树!\n");return ERROR;}
if(DestroyBiTree(T,BiTreeSet)==OK&&InitBiTree(T,BiTreeSet)==OK) return OK; //清空=销毁+初始化
else return ERROR;
}
//判定空二叉树:初始条件是二叉树T存在;操作结果是若T为空二叉树则返回TRUE,否则返回FALSE。
status BiTreeEmpty(BiTree T)
{
if(T==NULL) {printf("二叉树不存在!\n");return ERROR;}
else if(T->data==0x7fffffff) return TRUE; //TRUE:空二叉树
else return FALSE; //FALSE:非空二叉树
}
//求二叉树深度:初始条件是二叉树T存在;操作结果是返回T的深度。
int BiTreeDepth(BiTree T)
{
int Depth=0,Depth_L,Depth_R; //Depth_L:左子树深度;Depth_R:右子树深度
if(T==NULL) return 0; //return 0:二叉树不存在
else
{
Depth_L=BiTreeDepth(T->LeftChild); //递归:获取左子树深度
Depth_R=BiTreeDepth(T->RightChild); //递归:获取右子树深度
//以该节点为根节点的树的深度是左、右子树深度的最大值加一
Depth+=1+( Depth_L>Depth_R ? Depth_L:Depth_R);
return Depth; //返回深度
}
}
//获得根结点:初始条件是二叉树T已存在;操作结果是返回T的根。
Node *Root(BiTree T)
{
return T;
}
//获得结点:初始条件是二叉树T已存在,e是T中的某个结点;操作结果是返回e的值。
ElemType Value(BiTree T,Node *e)
{
return e->data; //返回该节点数据域的值
}
//结点赋值:初始条件是二叉树T已存在,e是T中的某个结点;操作结果是结点e赋值为value。
status Assign(BiTree T,Node *e,ElemType value)
{
e->data=value; //节点数据域赋值
return OK;
}
//获得双亲结点:初始条件是二叉树T已存在,e是T中的某个结点。
//操作结果是若e是T的非根结点,则返回它的双亲结点指针,否则返回NULL。
Node *Parent(BiTree T,Node *e)
{
Node *Queue[Set_MAXSIZE]={NULL}; //节点数组:存储层序遍历的每个结点指针
Node *LeftChild=NULL,*RightChild=NULL;
//游走监视哨:i指向数组的下一个空位,j指向正在遍历的节点的父亲节点
int i=1,j=0;
Queue[0]=T; //首先遍历根节点
while(i>j) //循环结束条件:j>=i
{
if (Queue[j]) //若Queue[j]!=NULL,访问其左右孩子节点
{
LeftChild=Queue[j]->LeftChild;
RightChild=Queue[j]->RightChild;
if(LeftChild) Queue[i++]=LeftChild; //存储j的非空左孩子结点,i++
if(RightChild) Queue[i++]=RightChild; //存储j的非空右孩子结点,i++
}
j++; //j指向上一层的下一个节点
}
//遍历该数组,寻找左孩子或者右孩子为e的节点
for(i=0;Queue[i]!=NULL;i++)
if(Queue[i]->LeftChild==e||Queue[i]->RightChild==e) break;
return Queue[i]; //返回该节点
}
//获得左孩子结点:初始条件是二叉树T存在,e是T中某个节点。
//操作结果是返回e的左孩子结点指针。若e无左孩子,则返回NULL。
Node *LeftChild(BiTree T,Node *e)
{
return e->LeftChild; //返回其左孩子节点
}
//获得右孩子结点:初始条件是二叉树T已存在,e是T中某个结点。
//操作结果是返回e的右孩子结点指针。若e无右孩子,则返回NULL。
Node *RightChild(BiTree T,Node *e)
{
return e->RightChild; //返回其右孩子节点
}
//获得左兄弟结点:初始条件是二叉树T存在,e是T中某个结点。
//操作结果是返回e的左兄弟结点指针。若e是T的左孩子或者无左兄弟,则返回NULL。
Node *LeftSibling(BiTree T,Node *e)
{
Node *parents=Parent(T,e); //parents:获取该节点的父亲节点
if(parents==NULL) return NULL; //ERROR:该节点没有父亲节点
//若该节点是其父亲节点的右孩子节点,返回该节点的左兄弟
if(parents->RightChild==e) return parents->LeftChild;
else return NULL; //ERROR:该节点是其父亲节点的左孩子节点
}
//获得右兄弟结点:初始条件是二叉树T已存在,e是T中某个结点。
//操作结果是返回e的右兄弟结点指针。若e是T的右孩子或者无右兄弟,则返回NULL。
Node *RightSibling(BiTree T,Node *e)
{
Node *parents=Parent(T,e); //parents:获取该节点的父亲节点
if(parents==NULL) return NULL; //ERROR:该节点没有父亲节点
//若该节点是其父亲节点的左孩子节点,返回该节点的右兄弟
if(parents->LeftChild==e) return parents->RightChild;
else return NULL; //ERROR:该节点是其父亲节点的右孩子节点
}
//插入子树:初始条件是二叉树T存在,p指向T中的某个结点,LR为0或1。
//非空二叉树c与T不相交且右子树为空。
//操作结果是根据LR为0或者1,插入c为T中p所指结点的左或右子树,p所指结点的原有左子树或右子树则为c的右子树。
status InsertChild(BiTree T,Node *p,int LR,BiTree c)
{
if(c->RightChild!=NULL) return ERROR; //ERROR:c的右子树不为空
if(LR==0) //插入到左子树
{
c->RightChild=p->LeftChild; //将p的左子树移动到c的右子树
p->LeftChild=c; //将c插入到p的左子树
}
else if(LR==1) //插入到右子树
{
c->RightChild=p->RightChild; //将p的右子树移动到c的右子树
p->RightChild=c; //将c插入到p的右子树
}
return OK;
}
//删除子树:初始条件是二叉树T存在,p指向T中的某个结点,LR为0或1。
//操作结果是根据LR为0或者1,删除c为T中p所指结点的左或右子树。
status DeleteChild(BiTree T,Node *p,int LR,BiTree BiTreeSet[])
{
if(LR==0) {DestroyBiTree(&(p->LeftChild),BiTreeSet);return OK;} //销毁左子树
else if(LR==1) {DestroyBiTree(&(p->RightChild),BiTreeSet);return OK;} //销毁右子树
else return ERROR; //ERROR:LR!=0&&LR!=1(输入错误)
}
//前序遍历:初始条件是二叉树T存在;操作结果:先序遍历T,对每个结点调用函数visit一次。
status PreOrderTraverse(BiTree T,status (*visit)(ElemType e))
{
if(T==NULL) return OK; //return OK:空树
visit(T->data); //访问该根节点
PreOrderTraverse(T->LeftChild,visit); //递归:遍历左子树
PreOrderTraverse(T->RightChild,visit); //递归:遍历右子树
return OK;
}
//中序遍历:初始条件是二叉树T存在;操作结果是中序遍历T,对每个结点调用函数visit一次。
status InOrderTraverse(BiTree T,status (*visit)(ElemType e))
{
if(T==NULL) return OK; //return OK:空树
InOrderTraverse(T->LeftChild,visit); //递归:遍历左子树
visit(T->data); //访问该根节点
InOrderTraverse(T->RightChild,visit); //递归:遍历右子树
return OK;
}
//后序遍历:初始条件是二叉树T存在;操作结果是后序遍历T,对每个结点调用函数visit一次。
status PostOrderTraverse(BiTree T,status (*visit)(ElemType e))
{
if(T==NULL) return OK; //return OK:空树
PostOrderTraverse(T->LeftChild,visit); //递归:遍历左子树
PostOrderTraverse(T->RightChild,visit); //递归:遍历右子树
visit(T->data); //访问该根节点
return OK;
}
//按层遍历:初始条件是二叉树T存在;操作结果是层序遍历T,对每个结点调用函数visit一次。
status LevelOrderTraverse(BiTree T,status (*visit)(ElemType e))
{
Node *Queue[Set_MAXSIZE]={NULL}; //节点数组:存储层序遍历的每个结点指针
Node *LeftChild=NULL,*RightChild=NULL;
//游走监视哨:i指向数组的下一个空位,j指向正在遍历的节点的父亲节点
int i=1,j=0;
Queue[0]=T; //首先遍历根节点
while(i>j) //循环结束条件:j>=i
{
if (Queue[j]) //若Queue[j]!=NULL,访问其左右孩子节点
{
visit(Queue[j]->data); //访问j指向的节点
LeftChild=Queue[j]->LeftChild;
RightChild=Queue[j]->RightChild;
if(LeftChild) Queue[i++]=LeftChild; //存储j的非空左孩子结点,i++
if(RightChild) Queue[i++]=RightChild; //存储j的非空右孩子结点,i++
}
j++; //j指向上一层的下一个节点
}
return OK;
}
/*
//按层遍历:初始条件是二叉树T存在;操作结果是层序遍历T,对每个结点调用函数visit一次。
status LevelOrderTraverse(BiTree T,status (*visit)(ElemType e))
{
int Depth=BiTreeDepth(T),Level_Destination; //获取二叉树的深度
for(Level_Destination=1;Level_Destination<=Depth;Level_Destination++)
LevelTraverse(T,1,Level_Destination,visit); //依次输出每一层
return OK;
}
//遍历输出二叉树的某一层:初始条件是二叉树T存在;操作结果是遍历输出二叉树的第i层。
//节点在第i层的另一层含义:从根节点到该节点的路径长度为i
status LevelTraverse(BiTree T,int Level_Now,int Level_Destination,status (*visit)(ElemType e))
{
if(T==NULL) return ERROR; //ERROR:该节点为空节点,返回
//若该节点在目标层,输出该节点
if(Level_Now==Level_Destination) visit(T->data);
else
{
//若该节点不在目标层,依次检查其左、右孩子节点是否在目标层(保证层序从左到右)
LevelTraverse(T->LeftChild,Level_Now+1,Level_Destination,visit);
LevelTraverse(T->RightChild,Level_Now+1,Level_Destination,visit);
}
return OK;
}
*/
//获取节点:初始条件是二叉树T存在;操作结果是获取第num个节点
status GetNode(BiTree T,int num,Node **node)
{
Node *Queue[Set_MAXSIZE]={NULL}; //节点数组:存储层序遍历的每个结点指针
Node *LeftChild=NULL,*RightChild=NULL;
//游走监视哨:i指向数组的下一个空位,j指向正在遍历的节点的父亲节点
int i=1,j=0;
Queue[0]=T; //首先遍历根节点
while(i>j) //循环结束条件:j>=i
{
if (Queue[j]) //若Queue[j]!=NULL,访问其左右孩子节点
{
LeftChild=Queue[j]->LeftChild;
RightChild=Queue[j]->RightChild;
if(LeftChild) Queue[i++]=LeftChild; //存储j的非空左孩子结点,i++
if(RightChild) Queue[i++]=RightChild; //存储j的非空右孩子结点,i++
}
j++; //j指向上一层的下一个节点
}
if(num<1||num>i) return ERROR; //ERROR:没有第num个节点
else {(*node)=Queue[num-1];return OK;} //将节点赋值给node,实现获取
}
//保存二叉树:初始条件是二叉树T存在且该二叉树不为空树;操作结果是将该二叉树保存到用户指定的文件中
status SaveBiTree(BiTree T)
{
char filename[FILENAME_LENGTH]={0};
FILE *fp=NULL;
if(T==NULL) {printf("二叉树不存在!\n");return ERROR;}
else if(T->data==0x7fffffff) {printf("该二叉树为空二叉树!\n");return ERROR;}
printf("输入文件路径(长度应在%d字符以内),将二叉树保存到该文件中:",FILENAME_LENGTH);
scanf("%s",filename); //输入文件路径
fp=fopen(filename,"w"); //打开文件
if(fp==NULL) return ERROR; //ERROR:打开文件错误
if(WriteNodeToFile(T,fp)==ERROR) return ERROR; //ERROR:写入节点错误
fclose(fp); //关闭文件
return OK;
}
//将节点写入文件:初始条件是文件已打开;操作结果是将该节点保存到用户指定的文件中
status WriteNodeToFile(BiTree T,FILE *fp)
{
if(fp==NULL) return ERROR; //ERROR:未打开文件
if(T==NULL) {fprintf(fp,"0 ");return OK;} //空节点:将0写入文件
else //非空节点
{
fprintf(fp,"%d ",T->data); //将该节点写入文件
WriteNodeToFile(T->LeftChild,fp); //递归:将该节点的左孩子结点写入文件
WriteNodeToFile(T->RightChild,fp); //递归:将该节点的右孩子结点写入文件
return OK;
}
}
//加载二叉树:初始条件是二叉树T存在;操作结果是将用户指定的文件中的二叉树加载出来
status LoadBiTree(BiTree *T,BiTree BiTreeSet[])
{
char filename[FILENAME_LENGTH]={0};
FILE *fp=NULL;
if((*T)==NULL) {printf("二叉树不存在!\n");return ERROR;}
if((*T)->data!=0x7fffffff) {*T=NULL;InitBiTree(T,BiTreeSet);} //若该二叉树不是空二叉树,初始化该二叉树
printf("输入文件路径(长度应在%d字符以内),加载文件中的二叉树:",FILENAME_LENGTH);
scanf("%s",filename); //输入文件路径
fp=fopen(filename,"r"); //打开文件
if(fp==NULL) return ERROR; //ERROR:打开文件错误
if(ReadNodeFromFile(T,fp)==ERROR) return ERROR; //ERROR:读取节点错误
fclose(fp); //关闭文件
return OK;
}
//加载文件中的节点:初始条件是文件已打开;操作结果是将用户指定的文件中的节点加载到二叉树T中
status ReadNodeFromFile(BiTree *T,FILE *fp)
{
ElemType Data;
if(fscanf(fp,"%d ",&Data)!=EOF&&Data!=0) //读取文件中二叉树节点的值(非0)
{
if((*T)==NULL) //若节点为NULL,创建节点
{
(*T)=(BiTree)malloc(sizeof(Node)); //分配空间
(*T)->LeftChild=(*T)->RightChild=NULL; //左右孩子节点指针置为NULL
if((*T)==NULL) exit(OVERFLOW); //ERROR:OVERFLOW
}
(*T)->data=Data; //节点数据域赋值
ReadNodeFromFile(&(*T)->LeftChild,fp); //递归:读取该节点的左孩子结点
ReadNodeFromFile(&(*T)->RightChild,fp); //递归:读取该节点的右孩子结点
return OK;
}
else return ERROR; //ERROR:ctrl+z或Data==0
}
//创建另一颗二叉树:初始条件是已经存在一颗二叉树
status CreatAnotherBiTree(BiTree *T,BiTree BiTreeSet[])
{
BiTree *Temp=T; //保存该二叉树头指针
if((*T)==NULL) {printf("没有已存在的二叉树,无需再次创建二叉树!\n");return ERROR;}
(*T)=NULL; //二叉树头指针置空
InitBiTree(T,BiTreeSet); //初始化该二叉树
printf("请输入先序遍历结果的二叉树,叶子节点的左右孩子节点为0:");
if(CreateBiTree(T)==OK) return OK; //创建二叉树
else //创建失败
{
DestroyBiTree(T,BiTreeSet); //销毁该二叉树
T=Temp; //将保存好的头指针赋值给T
return ERROR;
}
}
//选择二叉树:初始条件是已经存在至少一棵二叉树
status ChooseBiTree(BiTree *T,BiTree BiTreeSet[])
{
int i=0,n;
BiTree p=NULL;
if(BiTreeSet[0]==NULL) {printf("没有可选择的二叉树!\n");return ERROR;}
while(BiTreeSet[i]!=NULL) //遍历二叉树头指针数组
{
p=BiTreeSet[i];
printf("第%d个二叉树:",i+1);
if(p->data!=0x7fffffff) //若该二叉树不是空树
PreOrderTraverse(p,visit); //遍历该二叉树
printf("\n");
i++;
}
printf("输入n,选择第n个二叉树进行操作:");
scanf("%d",&n);
if(n>i||n<1) {printf("不存在第%d个二叉树!\n",n);return ERROR;}
(*T)=BiTreeSet[n-1]; //实现选择二叉树
if((*T)==NULL) return ERROR;
else return OK;
}
演示系统2(采用链式存储的线性表管理多个二叉树)
//Binary Tree on Linked Storage Structure
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
//编译预处理
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR -1
#define OVERFLOW -2
#define Set_MAXSIZE 50
#define FILENAME_LENGTH 30
//定义状态返回值类型和数据元素类型
typedef int status;
typedef int ElemType;
//定义二叉链表节点类型
typedef struct Node
{
struct Node *LeftChild;
ElemType data;
struct Node *RightChild;
}Node,*BiTree;
//定义多二叉树的头指针构成的链表节点类型
typedef struct BiTreeHeadPointer
{
BiTree head;
struct BiTreeHeadPointer *next;
}BiTreeHeadPointer,*BiTreeList;
//函数声明
status InitBiTree(BiTree *T,BiTreeList BiTree_L);
status DestroyBiTree(BiTree *T,BiTreeList BiTree_L);
status CreateBiTree(BiTree *T);
status ClearBiTree(BiTree *T,BiTreeList BiTree_L);
status BiTreeEmpty(BiTree T);
int BiTreeDepth(BiTree T);
Node *Root(BiTree T);
ElemType Value(BiTree T,Node *e);
status Assign(BiTree T,Node *e,ElemType value);
Node *Parent(BiTree T,Node *e);
Node *LeftChild(BiTree T,Node *e);
Node *RightChild(BiTree T,Node *e);
Node *LeftSibling(BiTree T,Node *e);
Node *RightSibling(BiTree T,Node *e);
status InsertChild(BiTree T,Node *p,int LR,BiTree c);
status DeleteChild(BiTree T,Node *p,int LR,BiTreeList BiTree_L);
status PreOrderTraverse(BiTree T,status (*visit)(ElemType e));
status InOrderTraverse(BiTree T,status (*visit)(ElemType e));
status PostOrderTraverse(BiTree T,status (*visit)(ElemType e));
status LevelOrderTraverse(BiTree T,status (*visit)(ElemType e));
//status LevelTraverse(BiTree T,int Level_Now,int Level_Destination,status (*visit)(ElemType e))
status GetNode(BiTree T,int num,Node **node);
status SaveBiTree(BiTree T);
status WriteNodeToFile(BiTree T,FILE *fp);
status LoadBiTree(BiTree *T,BiTreeList BiTree_L);
status ReadNodeFromFile(BiTree *T,FILE *fp);
status CreatAnotherBiTree(BiTree *T,BiTreeList BiTree_L);
status ChooseBiTree(BiTree *T,BiTreeList BiTree_L);
status visit (ElemType e)
{
printf(" %d ",e);
return OK;
}
int main(void)
{
int op=1,state; //op=1:程序入口,state:接受返回值,表示函数返回状态
int i,e,LR; //LR:左子树(LR=0),右子树(LR=1)
BiTree T=NULL; //T:二叉树名、头指针
Node *Temp=NULL,*NewTree=NULL;
//BiTree_L:二叉树的头指针构成的线性表
BiTreeList BiTree_L=(BiTreeList)malloc(sizeof(BiTreeHeadPointer));
BiTree_L->head=NULL;
BiTree_L->next=NULL;
while(op)
{
system("cls");
printf("\n\n");
printf(" Menu for Binary Tree on Linked Storage Structure \n");
printf("------------------------------------------------------------\n");
printf(" 0. Exit \n");
printf(" 1. InitBiTree 2. DestroyBiTree \n");
printf(" 3. CreateBiTree 4. ClearBiTree \n");
printf(" 5. BiTreeEmpty 6. BiTreeDepth \n");
printf(" 7. BiTreeRoot 8. GetNodeValue \n");
printf(" 9. AssignNode 10. GetNodeParent \n");
printf(" 11. GetLeftChild 12. GetRightChild \n");
printf(" 13. GetLeftSibling 14. GetRightSibling \n");
printf(" 15. InsertChild 16. DeleteChild \n");
printf(" 17. PreOrderTraverse 18. InOrderTraverse \n");
printf(" 19. PostOrderTraverse 20. LevelOrderTraverse \n");
printf(" 21. SaveBiTree 22. LoadBiTree \n");
printf(" 23. CreatAnothBiTree 24. ChooseBiTree \n");
printf("------------------------------------------------------------\n");
printf(" 请选择你的操作[0~24]:");
//若输入数字之外的字符,清空标准输入流里的数据,并将op置为default
if(scanf("%d",&op)!=1) {fflush(stdin);op=25;}
switch(op)
{
case 1:
state=InitBiTree(&T,BiTree_L);
if(state==OK) printf("二叉树初始化成功!\n");
else printf("二叉树已存在!\n");
system("pause");
break;
case 2:
if(T==NULL) printf("二叉树不存在,无需销毁!\n");
else
{
if(DestroyBiTree(&T,BiTree_L)==OK) printf("二叉树销毁成功!\n");
else printf("二叉树销毁失败!\n");
}
system("pause");
break;
case 3:
if(T==NULL) printf("二叉树不存在!\n");
else if(T->data!=0x7fffffff) printf("非空二叉树已存在!若要对其进行创建操作,请先清空该树!\n");
else
{
printf("请输入先序遍历结果的二叉树,叶子节点的左右孩子节点为0:");
if(CreateBiTree(&T)==OK) printf("创建成功!\n");
else printf("创建失败!\n");
}
system("pause");
break;
case 4:
state=ClearBiTree(&T,BiTree_L);
if(state==OK) printf("二叉树清空成功!\n");
else printf("二叉树清空失败!\n");
system("pause");
break;
case 5:
state=BiTreeEmpty(T);
if(state==TRUE) printf("该树是空树!\n");
else if(state==FALSE) printf("该树不是空树!\n");
system("pause");
break;
case 6:
if(T==NULL) printf("二叉树不存在!\n");
else if(T->data==0x7fffffff) printf("该二叉树为空二叉树!\n");
else printf("该二叉树的深度为%d!\n",BiTreeDepth(T));
system("pause");
break;
case 7:
if(T==NULL) printf("二叉树不存在!\n");
else if(T->data==0x7fffffff) printf("该二叉树为空二叉树!\n");
else printf("该二叉树根节点的数据元素为%d!\n",Root(T)->data);
system("pause");
break;
case 8:
if(T==NULL) printf("二叉树不存在!\n");
else if(T->data==0x7fffffff) printf("该二叉树为空二叉树!\n");
else
{
printf("输入i,获取二叉树层序遍历下第i个数据元素的值:");
scanf("%d",&i);
if(GetNode(T,i,&Temp)==ERROR) printf("输入错误,不存在第%d个数据元素!\n",i);
else
{
state=Value(T,Temp);
if(state==ERROR) printf("操作失败!\n");
else printf("二叉树层序遍历下第%d个数据元素的值为%d!\n",i,Value(T,Temp));
}
}
system("pause");
break;
case 9:
if(T==NULL) printf("二叉树不存在!\n");
else if(T->data==0x7fffffff) printf("该二叉树为空二叉树!\n");
else
{
printf("输入i,e,将二叉树层序遍历下第i个数据元素赋值为 e :");
scanf("%d%d",&i,&e);
if(GetNode(T,i,&Temp)==ERROR) printf("输入错误,不存在第%d个数据元素!\n",i);
else
{
state=Assign(T,Temp,e);
if(state==ERROR) printf("操作失败!\n");
else printf("赋值成功!\n");
}
}
system("pause");
break;
case 10:
if(T==NULL) printf("二叉树不存在!\n");
else if(T->data==0x7fffffff) printf("该二叉树为空二叉树!\n");
else
{
printf("输入i,获取二叉树层序遍历下第i个数据元素的父亲节点的值 :");
scanf("%d",&i);
if(GetNode(T,i,&Temp)==ERROR) printf("输入错误,不存在第%d个数据元素!\n",i);
else
{
Temp=Parent(T,Temp);
if(Temp==NULL) printf("操作失败!该节点没有父亲节点!\n");
else printf("二叉树层序遍历下第%d个数据元素的父亲节点的值为%d!\n",i,Value(T,Temp));
}
}
system("pause");
break;
case 11:
if(T==NULL) printf("二叉树不存在!\n");
else if(T->data==0x7fffffff) printf("该二叉树为空二叉树!\n");
else
{
printf("输入i,获取二叉树层序遍历下第i个数据元素的左孩子节点的值 :");
scanf("%d",&i);
if(GetNode(T,i,&Temp)==ERROR) printf("输入错误,不存在第%d个数据元素!\n",i);
else
{
Temp=LeftChild(T,Temp);
if(Temp==NULL) printf("操作失败!该节点没有左孩子节点!\n");
else printf("二叉树层序遍历下第%d个数据元素的左孩子节点的值为%d!\n",i,Value(T,Temp));
}
}
system("pause");
break;
case 12:
if(T==NULL) printf("二叉树不存在!\n");
else if(T->data==0x7fffffff) printf("该二叉树为空二叉树!\n");
else
{
printf("输入i,获取二叉树层序遍历下第i个数据元素的右孩子节点的值 :");
scanf("%d",&i);
if(GetNode(T,i,&Temp)==ERROR) printf("输入错误,不存在第%d个数据元素!\n",i);
else
{
Temp=RightChild(T,Temp);
if(Temp==NULL) printf("操作失败!该节点没有右孩子节点!\n");
else printf("二叉树层序遍历下第%d个数据元素的右孩子节点的值为%d!\n",i,Value(T,Temp));
}
}
system("pause");
break;
case 13:
if(T==NULL) printf("二叉树不存在!\n");
else if(T->data==0x7fffffff) printf("该二叉树为空二叉树!\n");
else
{
printf("输入i,获取二叉树层序遍历下第i个数据元素的左兄弟节点的值 :");
scanf("%d",&i);
if(GetNode(T,i,&Temp)==ERROR) printf("输入错误,不存在第%d个数据元素!\n",i);
else
{
Temp=LeftSibling(T,Temp);
if(Temp==NULL) printf("操作失败!该节点没有左兄弟节点!\n");
else printf("二叉树层序遍历下第%d个数据元素的左兄弟节点的值为%d!\n",i,Value(T,Temp));
}
}
system("pause");
break;
case 14:
if(T==NULL) printf("二叉树不存在!\n");
else if(T->data==0x7fffffff) printf("该二叉树为空二叉树!\n");
else
{
printf("输入i,获取二叉树层序遍历下第i个数据元素的右兄弟节点的值 :");
scanf("%d",&i);
if(GetNode(T,i,&Temp)==ERROR) printf("输入错误,不存在第%d个数据元素!\n",i);
else
{
Temp=RightSibling(T,Temp);
if(Temp==NULL) printf("操作失败!该节点没有右兄弟节点!\n");
else printf("二叉树层序遍历下第%d个数据元素的右兄弟节点的值为%d!\n",i,Value(T,Temp));
}
}
system("pause");
break;
case 15:
if(T==NULL) printf("二叉树不存在!\n");
else if(T->data==0x7fffffff) printf("该二叉树为空二叉树!\n");
else
{
printf("输入i,以二叉树层序遍历下第i个节点为根节点,插入新树 :");
scanf("%d",&i);
printf("请选择:\n0.插入到该节点的左子树。\n1.插入到该节点的右子树。\n");
scanf("%d",&LR);
if(GetNode(T,i,&Temp)==ERROR) printf("输入错误,不存在第%d个数据元素!\n",i);
else if(LR!=0&&LR!=1) printf("输入错误!请输入0或1以选择插入到该节点的左子树或右子树。\n");
else
{
//初始化NewTree,将NewTree置为空树
NewTree=(BiTree)malloc(sizeof(Node));
NewTree->data=0x7fffffff;
NewTree->LeftChild=NewTree->RightChild=NULL;
printf("请输入先序遍历结果的二叉树,叶子节点的左右孩子节点为0,同时保证该二叉树右子树为空:\n");
CreateBiTree(&NewTree);
state=InsertChild(T,Temp,LR,NewTree);
if(state==ERROR) printf("操作失败!\n");
else printf("操作成功!\n");
}
}
system("pause");
break;
case 16:
if(T==NULL) printf("二叉树不存在!\n");
else if(T->data==0x7fffffff) printf("该二叉树为空二叉树!\n");
else
{
printf("输入i,删除以二叉树层序遍历下第i个数据元素为根节点的子树:");
scanf("%d",&i);
printf("请选择:\n0.删除以该节点为根节点的左子树。\n1.删除以该节点为根节点的右子树。\n");
scanf("%d",&LR);
if(GetNode(T,i,&Temp)==ERROR) printf("输入错误,不存在第%d个数据元素!\n",i);
else
{
state=DeleteChild(T,Temp,LR,BiTree_L);
if(state==ERROR) printf("操作失败!\n");
else printf("删除成功!\n");
}
}
system("pause");
break;
case 17:
if(T==NULL) printf("二叉树不存在!\n");
else if(T->data==0x7fffffff) printf("该二叉树为空二叉树!\n");
else
{
if(PreOrderTraverse(T,visit)==OK) printf("\n前序遍历成功!\n");
else printf("\n前序遍历失败!\n");
}
system("pause");
break;
case 18:
if(T==NULL) printf("二叉树不存在!\n");
else if(T->data==0x7fffffff) printf("该二叉树为空二叉树!\n");
else
{
if(InOrderTraverse(T,visit)==OK) printf("\n中序遍历成功!\n");
else printf("\n中序遍历失败!\n");
}
system("pause");
break;
case 19:
if(T==NULL) printf("二叉树不存在!\n");
else if(T->data==0x7fffffff) printf("该二叉树为空二叉树!\n");
else
{
if(PostOrderTraverse(T,visit)==OK) printf("\n后序遍历成功!\n");
else printf("\n后序遍历失败!\n");
}
system("pause");
break;
case 20:
if(T==NULL) printf("二叉树不存在!\n");
else if(T->data==0x7fffffff) printf("该二叉树为空二叉树!\n");
else
{
if(LevelOrderTraverse(T,visit)==OK) printf("\n层序遍历成功!\n");
else printf("\n层序遍历失败!\n");
}
system("pause");
break;
case 21:
if(SaveBiTree(T)==OK) printf("二叉树保存成功!\n");
else printf("二叉树保存失败!\n");
system("pause");
break;
case 22:
if(LoadBiTree(&T,BiTree_L)==OK) printf("二叉树加载成功!\n");
else printf("二叉树加载失败!\n");
system("pause");
break;
case 23:
if(CreatAnotherBiTree(&T,BBiTree_L)==OK) printf("创建新的二叉树成功!\n");
else printf("操作失败!\n");
system("pause");
break;
case 24:
if(ChooseBiTree(&T,BiTree_L)==OK) printf("成功选中该二叉树!\n");
else printf("操作失败!\n");
system("pause");
break;
case 0:
break;
default:
printf("输入无效!菜单功能选择失败!\n");
system("pause");
break;
}//end of switch
}//end of while
printf("欢迎下次再使用本系统!\n");
return 0;
}//end of main()
//初始化二叉树:初始条件是二叉树T不存在;操作结果是构造空二叉树T。
//二叉树不存在:T==NULL
//空二叉树:T!=NULL&&T->data==0x7fffffff
status InitBiTree(BiTree *T,BiTreeList BiTree_L)
{
BiTreeHeadPointer *p=BiTree_L;
if ((*T)==NULL)
{
(*T)=(BiTree)malloc(sizeof(Node)); //为根节点分配空间
(*T)->data=0x7fffffff; //数据域置为0x7fffffff,将该树置为空二叉树
(*T)->LeftChild=(*T)->RightChild=NULL; //左右孩子节点指针置为NULL
while(p->next!=NULL) p=p->next; //遍历至表尾
p->next=(BiTreeList)malloc(sizeof(BiTreeHeadPointer)); //为节点分配空间
if(p->next==NULL) exit(OVERFLOW); //ERROR:OVERFLOW
p->next->head=(*T); //数据域录入该二叉树的表头指针
p->next->next=NULL; //指针域置空
return OK;
}
else return ERROR; //ERROR:二叉树已经存在
}
//销毁二叉树:初始条件是二叉树T已存在;操作结果是销毁二叉树T。
status DestroyBiTree(BiTree *T,BiTreeList BiTree_L)
{
BiTreeHeadPointer *pre_p=BiTree_L,*p=BiTree_L->next;//pre_p:p的前一个结点
if((*T)==NULL) return OK; //返回OK:空节点
DestroyBiTree(&(*T)->LeftChild,BiTree_L); //销毁左子树
DestroyBiTree(&(*T)->RightChild,BiTree_L); //销毁右子树
while(p!=NULL&&p->head!=(*T))
{
pre_p=p; //更新pre_p
p=p->next; //遍历至表尾
}
if(p!=NULL)
{
//若该二叉树在二叉树头指针构成的线性表上,将其从线性表中删除
pre_p->next=p->next; //执行删除操作
free(p); //释放节点空间
}
free(*T); //释放根节点
(*T)=NULL; //头指针置空
return OK;
}
//创建二叉树:初始条件是definition 给出二叉树T的定义;操作结果是按definition构造二叉树T。
status CreateBiTree(BiTree *T)
{
ElemType Data;
if(scanf("%d",&Data)!=EOF&&Data!=0) //输入二叉树节点的值(非0)
{
if((*T)==NULL) //若节点为NULL,创建节点
{
(*T)=(BiTree)malloc(sizeof(Node)); //分配空间
(*T)->LeftChild=(*T)->RightChild=NULL; //左右孩子节点指针置为NULL
if((*T)==NULL) exit(OVERFLOW); //ERROR:OVERFLOW
}
(*T)->data=Data; //节点数据域赋值
CreateBiTree(&(*T)->LeftChild); //递归:创建该节点的左子树
CreateBiTree(&(*T)->RightChild); //递归:创建该节点的右子树
return OK;
}
else return ERROR; //ERROR:根节点为0
}
//清空二叉树:初始条件是二叉树T存在;操作结果是将二叉树T清空。
status ClearBiTree(BiTree *T,BiTreeList BiTree_L)
{
if((*T)==NULL) {printf("二叉树不存在,无需清空!\n");return ERROR;}
else if((*T)->data==0x7fffffff) {printf("该二叉树为空二叉树!\n");return ERROR;}
if(DestroyBiTree(T,BiTree_L)==OK&&InitBiTree(T,BiTree_L)==OK) return OK; //清空=销毁+初始化
else return ERROR;
}
//判定空二叉树:初始条件是二叉树T存在;操作结果是若T为空二叉树则返回TRUE,否则返回FALSE。
status BiTreeEmpty(BiTree T)
{
if(T==NULL) {printf("二叉树不存在!\n");return ERROR;}
else if(T->data==0x7fffffff) return TRUE; //TRUE:空二叉树
else return FALSE; //FALSE:非空二叉树
}
//求二叉树深度:初始条件是二叉树T存在;操作结果是返回T的深度。
int BiTreeDepth(BiTree T)
{
int Depth=0,Depth_L,Depth_R; //Depth_L:左子树深度;Depth_R:右子树深度
if(T==NULL) return 0; //return 0:二叉树不存在
else
{
Depth_L=BiTreeDepth(T->LeftChild); //递归:获取左子树深度
Depth_R=BiTreeDepth(T->RightChild); //递归:获取右子树深度
//以该节点为根节点的树的深度是左、右子树深度的最大值加一
Depth+=1+( Depth_L>Depth_R ? Depth_L:Depth_R);
return Depth; //返回深度
}
}
//获得根结点:初始条件是二叉树T已存在;操作结果是返回T的根。
Node *Root(BiTree T)
{
return T;
}
//获得结点:初始条件是二叉树T已存在,e是T中的某个结点;操作结果是返回e的值。
ElemType Value(BiTree T,Node *e)
{
return e->data; //返回该节点数据域的值
}
//结点赋值:初始条件是二叉树T已存在,e是T中的某个结点;操作结果是结点e赋值为value。
status Assign(BiTree T,Node *e,ElemType value)
{
e->data=value; //节点数据域赋值
return OK;
}
//获得双亲结点:初始条件是二叉树T已存在,e是T中的某个结点。
//操作结果是若e是T的非根结点,则返回它的双亲结点指针,否则返回NULL。
Node *Parent(BiTree T,Node *e)
{
Node *Queue[Set_MAXSIZE]={NULL}; //节点数组:存储层序遍历的每个结点指针
Node *LeftChild=NULL,*RightChild=NULL;
//游走监视哨:i指向数组的下一个空位,j指向正在遍历的节点的父亲节点
int i=1,j=0;
Queue[0]=T; //首先遍历根节点
while(i>j) //循环结束条件:j>=i
{
if (Queue[j]) //若Queue[j]!=NULL,访问其左右孩子节点
{
LeftChild=Queue[j]->LeftChild;
RightChild=Queue[j]->RightChild;
if(LeftChild) Queue[i++]=LeftChild; //存储j的非空左孩子结点,i++
if(RightChild) Queue[i++]=RightChild; //存储j的非空右孩子结点,i++
}
j++; //j指向上一层的下一个节点
}
//遍历该数组,寻找左孩子或者右孩子为e的节点
for(i=0;Queue[i]!=NULL;i++)
if(Queue[i]->LeftChild==e||Queue[i]->RightChild==e) break;
return Queue[i]; //返回该节点
}
//获得左孩子结点:初始条件是二叉树T存在,e是T中某个节点。
//操作结果是返回e的左孩子结点指针。若e无左孩子,则返回NULL。
Node *LeftChild(BiTree T,Node *e)
{
return e->LeftChild; //返回其左孩子节点
}
//获得右孩子结点:初始条件是二叉树T已存在,e是T中某个结点。
//操作结果是返回e的右孩子结点指针。若e无右孩子,则返回NULL。
Node *RightChild(BiTree T,Node *e)
{
return e->RightChild; //返回其右孩子节点
}
//获得左兄弟结点:初始条件是二叉树T存在,e是T中某个结点。
//操作结果是返回e的左兄弟结点指针。若e是T的左孩子或者无左兄弟,则返回NULL。
Node *LeftSibling(BiTree T,Node *e)
{
Node *parents=Parent(T,e); //parents:获取该节点的父亲节点
if(parents==NULL) return NULL; //ERROR:该节点没有父亲节点
//若该节点是其父亲节点的右孩子节点,返回该节点的左兄弟
if(parents->RightChild==e) return parents->LeftChild;
else return NULL; //ERROR:该节点是其父亲节点的左孩子节点
}
//获得右兄弟结点:初始条件是二叉树T已存在,e是T中某个结点。
//操作结果是返回e的右兄弟结点指针。若e是T的右孩子或者无右兄弟,则返回NULL。
Node *RightSibling(BiTree T,Node *e)
{
Node *parents=Parent(T,e); //parents:获取该节点的父亲节点
if(parents==NULL) return NULL; //ERROR:该节点没有父亲节点
//若该节点是其父亲节点的左孩子节点,返回该节点的右兄弟
if(parents->LeftChild==e) return parents->RightChild;
else return NULL; //ERROR:该节点是其父亲节点的右孩子节点
}
//插入子树:初始条件是二叉树T存在,p指向T中的某个结点,LR为0或1。
//非空二叉树c与T不相交且右子树为空。
//操作结果是根据LR为0或者1,插入c为T中p所指结点的左或右子树,p所指结点的原有左子树或右子树则为c的右子树。
status InsertChild(BiTree T,Node *p,int LR,BiTree c)
{
if(c->RightChild!=NULL) return ERROR; //ERROR:c的右子树不为空
if(LR==0) //插入到左子树
{
c->RightChild=p->LeftChild; //将p的左子树移动到c的右子树
p->LeftChild=c; //将c插入到p的左子树
}
else if(LR==1) //插入到右子树
{
c->RightChild=p->RightChild; //将p的右子树移动到c的右子树
p->RightChild=c; //将c插入到p的右子树
}
return OK;
}
//删除子树:初始条件是二叉树T存在,p指向T中的某个结点,LR为0或1。
//操作结果是根据LR为0或者1,删除c为T中p所指结点的左或右子树。
status DeleteChild(BiTree T,Node *p,int LR,BiTreeList BiTree_L)
{
if(LR==0) {DestroyBiTree(&(p->LeftChild),BiTree_L);return OK;} //销毁左子树
else if(LR==1) {DestroyBiTree(&(p->RightChild),BiTree_L);return OK;} //销毁右子树
else return ERROR; //ERROR:LR!=0&&LR!=1(输入错误)
}
//前序遍历:初始条件是二叉树T存在;操作结果:先序遍历T,对每个结点调用函数visit一次。
status PreOrderTraverse(BiTree T,status (*visit)(ElemType e))
{
if(T==NULL) return OK; //return OK:空树
visit(T->data); //访问该根节点
PreOrderTraverse(T->LeftChild,visit); //递归:遍历左子树
PreOrderTraverse(T->RightChild,visit); //递归:遍历右子树
return OK;
}
//中序遍历:初始条件是二叉树T存在;操作结果是中序遍历T,对每个结点调用函数visit一次。
status InOrderTraverse(BiTree T,status (*visit)(ElemType e))
{
if(T==NULL) return OK; //return OK:空树
InOrderTraverse(T->LeftChild,visit); //递归:遍历左子树
visit(T->data); //访问该根节点
InOrderTraverse(T->RightChild,visit); //递归:遍历右子树
return OK;
}
//后序遍历:初始条件是二叉树T存在;操作结果是后序遍历T,对每个结点调用函数visit一次。
status PostOrderTraverse(BiTree T,status (*visit)(ElemType e))
{
if(T==NULL) return OK; //return OK:空树
PostOrderTraverse(T->LeftChild,visit); //递归:遍历左子树
PostOrderTraverse(T->RightChild,visit); //递归:遍历右子树
visit(T->data); //访问该根节点
return OK;
}
//按层遍历:初始条件是二叉树T存在;操作结果是层序遍历T,对每个结点调用函数visit一次。
status LevelOrderTraverse(BiTree T,status (*visit)(ElemType e))
{
Node *Queue[Set_MAXSIZE]={NULL}; //节点数组:存储层序遍历的每个结点指针
Node *LeftChild=NULL,*RightChild=NULL;
//游走监视哨:i指向数组的下一个空位,j指向正在遍历的节点的父亲节点
int i=1,j=0;
Queue[0]=T; //首先遍历根节点
while(i>j) //循环结束条件:j>=i
{
if (Queue[j]) //若Queue[j]!=NULL,访问其左右孩子节点
{
visit(Queue[j]->data); //访问j指向的节点
LeftChild=Queue[j]->LeftChild;
RightChild=Queue[j]->RightChild;
if(LeftChild) Queue[i++]=LeftChild; //存储j的非空左孩子结点,i++
if(RightChild) Queue[i++]=RightChild; //存储j的非空右孩子结点,i++
}
j++; //j指向上一层的下一个节点
}
return OK;
}
/*
//按层遍历:初始条件是二叉树T存在;操作结果是层序遍历T,对每个结点调用函数visit一次。
status LevelOrderTraverse(BiTree T,status (*visit)(ElemType e))
{
int Depth=BiTreeDepth(T),Level_Destination; //获取二叉树的深度
for(Level_Destination=1;Level_Destination<=Depth;Level_Destination++)
LevelTraverse(T,1,Level_Destination,visit); //依次输出每一层
return OK;
}
//遍历输出二叉树的某一层:初始条件是二叉树T存在;操作结果是遍历输出二叉树的第i层。
//节点在第i层的另一层含义:从根节点到该节点的路径长度为i
status LevelTraverse(BiTree T,int Level_Now,int Level_Destination,status (*visit)(ElemType e))
{
if(T==NULL) return ERROR; //ERROR:该节点为空节点,返回
//若该节点在目标层,输出该节点
if(Level_Now==Level_Destination) visit(T->data);
else
{
//若该节点不在目标层,依次检查其左、右孩子节点是否在目标层(保证层序从左到右)
LevelTraverse(T->LeftChild,Level_Now+1,Level_Destination,visit);
LevelTraverse(T->RightChild,Level_Now+1,Level_Destination,visit);
}
return OK;
}
*/
//获取节点:初始条件是二叉树T存在;操作结果是获取第num个节点
status GetNode(BiTree T,int num,Node **node)
{
Node *Queue[Set_MAXSIZE]={NULL}; //节点数组:存储层序遍历的每个结点指针
Node *LeftChild=NULL,*RightChild=NULL;
//游走监视哨:i指向数组的下一个空位,j指向正在遍历的节点的父亲节点
int i=1,j=0;
Queue[0]=T; //首先遍历根节点
while(i>j) //循环结束条件:j>=i
{
if (Queue[j]) //若Queue[j]!=NULL,访问其左右孩子节点
{
LeftChild=Queue[j]->LeftChild;
RightChild=Queue[j]->RightChild;
if(LeftChild) Queue[i++]=LeftChild; //存储j的非空左孩子结点,i++
if(RightChild) Queue[i++]=RightChild; //存储j的非空右孩子结点,i++
}
j++; //j指向上一层的下一个节点
}
if(num<1||num>i) return ERROR; //ERROR:没有第num个节点
else {(*node)=Queue[num-1];return OK;} //将节点赋值给node,实现获取
}
//保存二叉树:初始条件是二叉树T存在且该二叉树不为空树;操作结果是将该二叉树保存到用户指定的文件中
status SaveBiTree(BiTree T)
{
char filename[FILENAME_LENGTH]={0};
FILE *fp=NULL;
if(T==NULL) {printf("二叉树不存在!\n");return ERROR;}
else if(T->data==0x7fffffff) {printf("该二叉树为空二叉树!\n");return ERROR;}
printf("输入文件路径(长度应在%d字符以内),将二叉树保存到该文件中:",FILENAME_LENGTH);
scanf("%s",filename); //输入文件路径
fp=fopen(filename,"w"); //打开文件
if(fp==NULL) return ERROR; //ERROR:打开文件错误
if(WriteNodeToFile(T,fp)==ERROR) return ERROR; //ERROR:写入节点错误
fclose(fp); //关闭文件
return OK;
}
//将节点写入文件:初始条件是文件已打开;操作结果是将该节点保存到用户指定的文件中
status WriteNodeToFile(BiTree T,FILE *fp)
{
if(fp==NULL) return ERROR; //ERROR:未打开文件
if(T==NULL) {fprintf(fp,"0 ");return OK;} //空节点:将0写入文件
else //非空节点
{
fprintf(fp,"%d ",T->data); //将该节点写入文件
WriteNodeToFile(T->LeftChild,fp); //递归:将该节点的左孩子结点写入文件
WriteNodeToFile(T->RightChild,fp); //递归:将该节点的右孩子结点写入文件
return OK;
}
}
//加载二叉树:初始条件是二叉树T存在;操作结果是将用户指定的文件中的二叉树加载出来
status LoadBiTree(BiTree *T,BiTreeList BiTree_L)
{
char filename[FILENAME_LENGTH]={0};
FILE *fp=NULL;
if((*T)==NULL) {printf("二叉树不存在!\n");return ERROR;}
if((*T)->data!=0x7fffffff) {*T=NULL;InitBiTree(T,BiTree_L);} //若该二叉树不是空二叉树,初始化该二叉树
printf("输入文件路径(长度应在%d字符以内),加载文件中的二叉树:",FILENAME_LENGTH);
scanf("%s",filename); //输入文件路径
fp=fopen(filename,"r"); //打开文件
if(fp==NULL) return ERROR; //ERROR:打开文件错误
if(ReadNodeFromFile(T,fp)==ERROR) return ERROR; //ERROR:读取节点错误
fclose(fp); //关闭文件
return OK;
}
//加载文件中的节点:初始条件是文件已打开;操作结果是将用户指定的文件中的节点加载到二叉树T中
status ReadNodeFromFile(BiTree *T,FILE *fp)
{
ElemType Data;
if(fscanf(fp,"%d ",&Data)!=EOF&&Data!=0) //读取文件中二叉树节点的值(非0)
{
if((*T)==NULL) //若节点为NULL,创建节点
{
(*T)=(BiTree)malloc(sizeof(Node)); //分配空间
(*T)->LeftChild=(*T)->RightChild=NULL; //左右孩子节点指针置为NULL
if((*T)==NULL) exit(OVERFLOW); //ERROR:OVERFLOW
}
(*T)->data=Data; //节点数据域赋值
ReadNodeFromFile(&(*T)->LeftChild,fp); //递归:读取该节点的左孩子结点
ReadNodeFromFile(&(*T)->RightChild,fp); //递归:读取该节点的右孩子结点
return OK;
}
else return ERROR; //ERROR:ctrl+z或Data==0
}
//创建另一颗二叉树:初始条件是已经存在一颗二叉树
status CreatAnotherBiTree(BiTree *T,BiTreeList BiTree_L)
{
BiTree *Temp=T; //保存该二叉树头指针
if((*T)==NULL) {printf("没有已存在的二叉树,无需再次创建二叉树!\n");return ERROR;}
(*T)=NULL; //二叉树头指针置空
InitBiTree(T,BiTree_L); //初始化该二叉树
printf("请输入先序遍历结果的二叉树,叶子节点的左右孩子节点为0:");
if(CreateBiTree(T)==OK) return OK; //创建二叉树
else //创建失败
{
DestroyBiTree(T,BiTree_L); //销毁该二叉树
T=Temp; //将保存好的头指针赋值给T
return ERROR;
}
}
//选择二叉树:初始条件是已经存在至少一棵二叉树
status ChooseBiTree(BiTree *T,BiTreeList BiTree_L)
{
int i=1,n;
BiTreeHeadPointer *p=BiTree_L->next;
if(p==NULL) {printf("没有可选择的二叉树!\n");return ERROR;}
while(p!=NULL) //遍历二叉树头指针链表
{
printf("第 %d 个二叉树:",i);
if(p->head->data!=0x7fffffff) //若该二叉树不是空树
PreOrderTraverse(p->head,visit); //遍历该二叉树
printf("\n"); //格式控制
p=p->next;
i++;
}
printf("输入n,选择第n个二叉树进行操作:");
scanf("%d",&n);
if(n>i-1||n<1) {printf("不存在第 %d 个二叉树!\n",n);return ERROR;}
i=1;
p=BiTree_L->next;
while(i!=n) {p=p->next;i++;} //遍历二叉树头指针链表
(*T)=p->head; //实现二叉树的选择
return OK;
}
演示系统相关问题的说明
- 这个简单的演示系统是用纯C语言完成的。这也就衍生了以下的一些的问题与说明:
- “创建二叉树”函数中,采取了文件尾作为结束标记。正确理解文件尾EOF与CTRL+Z,请参考 :C语言中EOF与Ctrl+Z。
- 由于C语言中不能使用“引用”,而某些函数需要改变传入参数的值,故在系统中多次使用指针以及指向指针的指针,导致可读性变差。(部分可以考虑使用全局变量代替。)
- 文件路径使用了scanf()函数和字符数组输入,这意味着文件路径与文件名不能含有空格,并且有长度限制。
scanf("%s",filename);
- C语言中产生函数符号的规则是根据名称产生,这也就注定了C语言不存在函数重载的概念。函数不能重载导致了本系统的实用性受限。关于C语言与C++,请参考:C语言与C++
- 本系统多次使用scanf与fscanf函数。这两个函数会导致IDE报“不安全(unsafe)”的警告(warning),甚至在某些IDE会报错(error)。
warning C4996: ‘scanf’: This function or variable may be unsafe. Consider using scanf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
warning C4996: ‘fscanf’: This function or variable may be unsafe. Consider using fscanf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
- 代码风格的说明
- 作为数据结构初学者,我几乎在每一行都写了详细的注释。
- 本演示系统源于《数据结构(C语言版)》(严蔚敏等著),但没有太多地参考书上的代码和思路,绝大部分是自己的理解。这意味着,不是所有的函数都是最优的函数。
- 本演示系统是严格按照实验要求完成的,对系统的完备性和算法的健壮性做了一定的考虑,对函数的初始条件进行了了严格的控制(例如区分“空二叉树”与“二叉树不存在”等),对输入输出操作都进行了提示,甚至显得有些繁琐。
- 由于实际的需要,在编写演示系统时,对一些函数的参数进行了调整。甚至还将某些操作拆分成两个函数,所以有些函数可能不符合函数之间应该满足的“高内聚,低耦合”的要求。
- 在编写代码时保留了一些个人习惯,如括号配对对齐等,敬请谅解。
实验测试参考用例
1 2 4 6 0 0 7 0 0 5 0 0 3 0 0
1 3 4 9 99 0 0 20 0 0 11 13 0 0 14 0 0 7 0 0 6 2 0 0 5 0 0
1 2 4 0 0 5 0 0 3 6 0 0 7 0 0
999 20 40 0 0 50 70 0 0 0 1 0 0
10 20 40 0 0 50 70 0 0 0 30 0 60 0 0
10 20 0 50 70 0 0 0 30 0 60 0 0
参考文献
[1] 严蔚敏等. 数据结构(C语言版). 清华大学出版社
[2] Larry Nyhoff. ADTs, Data Structures, and Problem Solving with C++. Second Edition, Calvin College, 2005
[3] 殷立峰. Qt C++跨平台图形界面程序设计基础. 清华大学出版社,2014:192~197
[4] 严蔚敏等.数据结构题集(C语言版). 清华大学出版社