链式二叉树的前序创建、递归前序遍历、非递归堆栈前序遍历、前序销毁以及求二叉树的深度
需要完整代码可到CSDN资源页搜索链式二叉树的前序创建、递归遍历、利用堆栈的非递归遍历、前序销毁以及求二叉树的深度进行下载
1、数据结构
typedef char ElemType;
typedef struct BiTree{
ElemType data;
struct BiTree *parent;
struct BiTree *leftchild;
struct BiTree *rightchild;
}BiTree;
2、递归创建前序二叉树
(1)先创建树的根结点
(2)再创建根结点的左子树,左子树也时一棵树,同样需要按照前序顺序进行创建,则又回到(1),这就是一个递归的过程,若左子树为空,则不需要进行递归。
(3)左子树创建完毕后,最后创建右子树,右子树也是一棵树,同样需要按照前序顺序进行创建,也回到(1),这也是一个递归的过程,若右子树为空,也不需要进行递归。若左右子树同时为空,则表示该结点为叶子结点。
d
╱ ╲
c e
╱ ╲
b f
╱ ╲
a g
(1)首先输入结点d,//按先序次序输入二叉树中结点的值(一个字符),'.'表示空树
//对于先序输入,每个叶子结点的左右结点都先输入'.',即在输入一个叶子结点后,要连续输入两个'.'程序才能认定这个结点为叶子结点
BiTree *PreCreateBiTree( )
{
BiTree *tree;
tree = NodeMalloc( ); //为该结点分配空间
printf( "请输入结点的值:\n" );
scanf( "%s", &(tree->data) ); //输入结点的值
if( tree->data == '.' ){ //如果输入为字符型的'.',表示该结点为空,将为该结点分配的空间释放,并返回NULL
free( tree );
tree = NULL;
}
else{
tree->leftchild = PreCreateBiTree( ); //生成当前树的根结点的左子树
if( tree->leftchild ) //如果当前结点的左子树不为空
tree->leftchild->parent = tree; //则左子树的根结点的父节点即为当前结点
tree->rightchild = PreCreateBiTree( );//生成当前树的根结点的右子树
if( tree->rightchild ) //如果当前结点的右子树不为空
tree->rightchild->parent = tree;//则右子树的根结点的父节点即为当前结点
}
return tree;
}
3、利用递归的前序遍历
递归的前序遍历与创建函数的逻辑相似:
(1)首先判断树是否为空,若树为空,则打印树为空的信息
(2)若树不为空,判断当前结点是否为叶子结点(叶子结点的左右子树都为空),若是叶子结点,则打印叶子结点的值域
(3)若树不为空,且当前结点不是叶子结点,则先打印当前结点的值域。
(注意:下面的1)和2)要么其中一个发生,要么都发生,因为当前结点不是叶子结点,所以它至少存在一个子树)
1)若左子树不为空,递归调用自身打印左子树的各结点的值域
2)若右子树不为空,递归调用自身打印右子树的各结点的值域
void PreOrderTraverse( BiTree *tree, void( *visit )( BiTree *node ) )
{
if( tree == NULL )
printf( "the tree is empty!" );
else if( tree->leftchild == NULL && tree->rightchild == NULL ){ //当前结点为叶子结点则输出当前结点的值
visit( tree );
}
else{ //当前结点不是叶子结点,则先打印当前结点的值,
visit( tree );
if( tree->leftchild ){ //若当前结点的左子树不为空,则再按先序打印其左子树的结点
PreOrderTraverse( tree->leftchild, visit );
}
if( tree->rightchild ){ //如果当前树的根结点的右子树不为空,再按先序打印其右子树的结点
PreOrderTraverse( tree->rightchild, visit );
}
}
}
4、利用堆栈实现非递归的前序遍历
(1)首先判断树是否为空,若树为空,则打印树为空的信息
(2)若树不为空:
1)访问当前树的根结点
2)判断当前根结点的右子树是否为空,若不为空,将右子树的根结点压栈
3)判断当前根结点的左子树是否为空:
A.若左子树为空:
a)若栈为空,表示当前树的结点都已经访问完毕,则访问结束
b)若栈不为空,则弹出栈顶,回到1)对栈顶结点进行访问,此时的栈顶为当前结点的右子树的根结点
需要注意的是: 栈使用完毕之后,一定要销毁,不然会造成内存泄露,可能造成下次运行时出错。b.若左子树不为空,回到1)对左子树进行访问
//利用栈结构实现非递归的前序遍历
void PreOrderTraverse_stack( BiTree *tree, void( *visit )( BiTree *node ))
{
BiTree *current;
Lstack *stack;
BiTree right;
current = tree; //将current指向当前的树根结点
stack = InitStack( ); //将栈初始化为空栈
if( current == NULL )
printf( "the tree is empty!" );
while( !StackEmpty( stack ) || current ){ //栈不为空或者current不为空时进入循环,即还有没有访问完结点时进入循环
visit( current ); //访问当前结点
if( current->rightchild ){ //如果current的右子树不为空
Push( stack, *current->rightchild ); //将current的右子树的结点压栈
}
if( current->leftchild == NULL ){ //如果current的左子树为空,
if( StackEmpty( stack ) ) //且栈也为空(栈中存的是右子树的根结点,栈为空表示右子树为空)
break; //结束循环
Pop( stack, &right ); //如果栈不为空,弹出栈顶,访问右子树
current = &right;
}
else{ //如果current的左子树不为空,访问左子树
current = current->leftchild;
}
} //当栈为空且current为空时当前树的所有结点都被访问过,只有栈为空时表示当前树的左子树的所有结点都遍历完了
DestroyStack( stack );//栈使用完毕之后,一定要销毁,不然会造成内存泄露
}
5、判断树的深度
1)计算左子树的深度depth_l,递归调用相当于又从(1)开始
2)计算右子树的深度depth_r,递归调用相当于又从(1)开始
3)比较两个深度值的大小,返回较大值加1为当前树的深度
//判断树的深度
int BiTreeDepth( BiTree *tree )
{
int depth_r;
int depth_l;
if( tree == NULL ) //如果为空树,则返回0,表示树的深度为0
return 0;
else{ //否则计算当前树的左右子树的深度
depth_l = BiTreeDepth( tree->leftchild );
depth_r = BiTreeDepth( tree->rightchild );
if( depth_l > depth_r ) //当前树的深度为其左右子树深度较大的值加1
return depth_l + 1;
else
return depth_r + 1;
}
}
6、先序顺序销毁一个树
1)若为叶子结点,则释放该结点2)若不为叶子结点,将该结点的左右子树的根结点保留下来,释放该结点,需要注意的是:由于当前释放的结点不是叶子结点,所以a和b至少有一个会发生。
a.若左子树不为空,则递归调用函数自身销毁左子树,此时左子树不为空,所以进入调用函数后会回到(2)b.若右子树不为空,则递归调用函数自身销毁右子树,此时右子树不为空,所以进入调用函数后会回到(2)
void PreDestroyBiTree( BiTree *tree )
{
BiTree *left;
BiTree *right;
if( tree == NULL ){ //如果树为空树,则进行打印
printf( "DestroyBiTree:the tree is empty!\n" );
}
else if( tree->leftchild == NULL && tree->rightchild == NULL ){ //如果当前结点为叶子结点,则释放为其分配的空间
free( tree );
tree = NULL;
}
else{
left = tree->leftchild; //用两个指针分别指向当前结点的左右子树的根结点,
right = tree->rightchild;
free( tree ); //释放当前根结点
tree = NULL;
if( left ){ //如果当前结点的左子树不为空,则先释放左子树的各个结点
PreDestroyBiTree( left );
left = NULL; //左子树的空间释放完毕后,将指向左子树根结点的指针置为空
}
if( right ){//如果当前结点的右子树不为空,则再释放右子树的各个结点
PreDestroyBiTree( right );
right = NULL;//右子树的空间释放完毕后,将指向右子树根结点的指针置为空
}
}
}<span style="white-space:pre"> </span>
7、为了完整性在此给出其他相关数据结构和函数
#include"bitree.h"
#include< stdlib.h >
#include< stdio.h >
//初始化栈空间
Lstack *InitStack( )
{
Lstack *S;
S = ( Lstack * )malloc( sizeof( Lstack ) );
if( !S ){
printf( "OVERFLOW!\n");
exit( EXIT_FAILURE );
}
S->link = NULL;
return S;
}
//销毁栈
void DestroyStack( Lstack *S )
{
ClearStack( S );
free( S );
}
//清空栈
void ClearStack( Lstack *S )
{
Lstack *top;
top = S->link;
while( top != NULL ){
S->link = S->link->link;
free( top );
top = S->link;
}
S->link = NULL;
}
//压栈
void Push( Lstack *S, LElemType e )
{
Lstack *first;
first = ( Lstack * )malloc( sizeof( Lstack ) );
first->data = e;
first->link = S->link;
S->link = first;
}
//弹栈
void Pop( Lstack *S, LElemType *e )
{
if( S->link == NULL ){
printf( "the stack is empty!\n");
exit( EXIT_FAILURE );
}
*e = S->link->data;
S->link = S->link->link;
}<pre name="code" class="cpp">//栈中元素个数
int Length( Lstack *S )
{
int count = 0;
Lstack *current = S->link;
while( current != NULL ){
count++;
current = current->link;
}
return count;
}
//判断栈是否为空,若为空,返回TRUE,非空返回FALSE
int StackEmpty( Lstack *S )
{
if( Length( S ) )
return 0;
return 1;
}
(2)头文件bitree.h
<pre name="code" class="cpp">#define TRUE 1
#define FALSE 0
//树的数据结构
typedef char ElemType;
typedef struct BiTree{
ElemType data;
struct BiTree *parent;
struct BiTree *leftchild;
struct BiTree *rightchild;
}BiTree;
//堆栈的数据结构
typedef BiTree LElemType;
typedef struct temp{
struct temp *link;
LElemType data;
}Lstack;
Lstack * InitStack( );//初始化栈为空栈
void DestroyStack( Lstack *S );//将栈销毁
void ClearStack( Lstack *S ); //将栈清空
int GetTop( Lstack *S, LElemType *e );//获得栈顶元素,保持栈的状态不变
void Push( Lstack *S, LElemType e );//并将元素e压入栈顶
void Pop( Lstack *S, LElemType *e ); //弹栈,并将出栈元素的值赋给e
int Length( Lstack *S );//栈中元素个数
int StackEmpty( Lstack *S );//判断栈是否为空,若为空,返回TRUE,非空返回FALSE
void Print( Lstack *S ); //打印栈中的所有元素
BiTree *NodeMalloc( ); //为一个树结点分配空间
BiTree *InitBiTree( ); //构造空的二叉树
BiTree *PreCreateBiTree( ); //按先序次序输入二叉树中结点的值(一个字符),字符型的句号'.'表示为空树
void PreDestroyBiTree( BiTree *tree ); //按先序次序销毁树
void PreOrderTraverse( BiTree *tree, void( *visit )( BiTree *node ) ); //递归的前序遍历,按先序次序输出二叉树中各个结点的值(字符型)
void PrintElem( BiTree *node );//打印结点值
void PreOrderTraverse_stack( BiTree *tree, void( *visit )( BiTree *node ));//利用堆栈的非递归的前序遍历
int BiTreeEmpty( BiTree *tree );//判断树是否为空
int BiTreeDepth( BiTree *tree );//求树的深度
(3)树函数bitree.c
#include<stdlib.h>
#include<stdio.h>
#include"bitree.h"
BiTree *InitBiTree( ) //构造空的二叉树
{
return NULL;
}
BiTree * NodeMalloc( ) //为一个树结点分配空间并将结点进行初始化
{
BiTree *node;
node = ( BiTree * )malloc( sizeof( BiTree ) );
if( node == NULL ){
printf( "NodeMalloc:OVERFLOW\n" );
exit( EXIT_FAILURE );
}
node->leftchild = NULL; //初始化结点的各个指针域为NULL
node->rightchild = NULL;
node->parent = NULL;
return node;
}
//按先序次序输入二叉树中结点的值(一个字符),'.'表示空树
//对于先序输入,每个叶子结点的左右结点都先输入'.',即在输入一个叶子结点后,要连续输入两个'.'程序才能认定这个结点为叶子结点
BiTree *PreCreateBiTree( )
{
BiTree *tree;
tree = NodeMalloc( ); //为该结点分配空间
printf( "请输入结点的值:\n" );
scanf( "%s", &(tree->data) ); //输入结点的值
if( tree->data == '.' ){ //如果输入为字符型的'.',表示该结点为空,将为该结点分配的空间释放,并返回NULL
free( tree );
tree = NULL;
}
else{
tree->leftchild = PreCreateBiTree( ); //生成当前树的根结点的左子树
if( tree->leftchild ) //如果当前结点的左子树不为空
tree->leftchild->parent = tree; //则左子树的根结点的父节点即为当前结点
tree->rightchild = PreCreateBiTree( );//生成当前树的根结点的右子树
if( tree->rightchild ) //如果当前结点的右子树不为空
tree->rightchild->parent = tree;//则右子树的根结点的父节点即为当前结点
}
return tree;
}
//先序顺序销毁一个树,即先释放根结点,再释放左右子树(左右子树的释放顺序可以调换)
void PreDestroyBiTree( BiTree *tree )
{
BiTree *left;
BiTree *right;
if( tree == NULL ){ //如果树为空树,则进行打印
printf( "DestroyBiTree:the tree is empty!\n" );
}
else if( tree->leftchild == NULL && tree->rightchild == NULL ){ //如果当前结点为叶子结点,则释放为其分配的空间
free( tree );
tree = NULL;
}
else{
left = tree->leftchild; //用两个指针分别指向当前结点的左右子树的根结点,
right = tree->rightchild;
free( tree ); //释放当前结点
tree = NULL;
if( left ){ //如果当前结点的左子树不为空,则先释放左子树的各个结点
PreDestroyBiTree( left );
left = NULL; //左子树的空间释放完毕后,将指向左子树根结点的指针置为空
}
if( right ){//如果当前结点的右子树不为空,则再释放右子树的各个结点
PreDestroyBiTree( right );
right = NULL;//右子树的空间释放完毕后,将指向右子树根结点的指针置为空
}
}
}
//递归的前序遍历,按先序次序输出二叉树中各个结点的值(字符型)
void PreOrderTraverse( BiTree *tree, void( *visit )( BiTree *node ) )
{
if( tree == NULL )
printf( "the tree is empty!" );
else if( tree->leftchild == NULL && tree->rightchild == NULL ){ //当前结点为叶子结点则输出当前结点的值
visit( tree );
}
else{ //当前结点不是叶子结点,则先打印当前结点的值,
visit( tree );
if( tree->leftchild ){ //若当前结点的左子树不为空,则再按先序打印其左子树的结点
PreOrderTraverse( tree->leftchild, visit );
}
if( tree->rightchild ){ //如果当前树的根结点的右子树不为空,再按先序打印其右子树的结点
PreOrderTraverse( tree->rightchild, visit );
}
}
}
//打印树结点的data域
void PrintElem( BiTree *node )
{
if( node ){
printf( "%c", node->data );
}
}
//利用栈结构实现非递归的前序遍历
void PreOrderTraverse_stack( BiTree *tree, void( *visit )( BiTree *node ))
{
BiTree *current;
Lstack *stack;
BiTree right;
current = tree; //将current指向当前的树根结点
stack = InitStack( ); //将栈初始化为空栈
if( current == NULL )
printf( "the tree is empty!" );
while( !StackEmpty( stack ) || current ){ //栈不为空或者current不为空时进入循环,即还有没有访问完结点时进入循环
visit( current ); //访问当前结点
if( current->rightchild ){ //如果current的右子树不为空
Push( stack, *current->rightchild ); //将current的右子树的结点压栈
}
if( current->leftchild == NULL ){ //如果current的左子树为空,
if( StackEmpty( stack ) ) //且栈也为空(栈中存的是右子树的根结点,栈为空表示右子树为空)
break; //结束循环
Pop( stack, &right ); //如果栈不为空,弹出栈顶,访问右子树
current = &right;
}
else{ //如果current的左子树不为空,访问左子树
current = current->leftchild;
}
} //当栈为空且current为空时当前树的所有结点都被访问过,只有栈为空时表示当前树的左子树的所有结点都遍历完了
DestroyStack( stack );
}
int BiTreeEmpty( BiTree *tree )
{
if( tree == NULL )
return TRUE;
else
return FALSE;
}
//判断树的深度
int BiTreeDepth( BiTree *tree )
{
int depth_r;
int depth_l;
if( tree == NULL ) //如果为空树,则返回0,表示树的深度为0
return 0;
else{ //否则计算当前树的左右子树的深度
depth_l = BiTreeDepth( tree->leftchild );
depth_r = BiTreeDepth( tree->rightchild );
if( depth_l > depth_r ) //当前树的深度为其左右子树深度较大的值加1
return depth_l + 1;
else
return depth_r + 1;
}
}
(4)主函数:main.c
#include< stdio.h >
#include"bitree.h"
void main()
{
BiTree *bi;
int depth;
printf( "先序顺序构建二叉树,对于叶子结点在其后要连续输入两个'.'\n" );
printf( "以标志该结点为叶子结点,'.'表示空结点\n" );
bi = PreCreateBiTree( );
printf( "*****PreOrderTraverse*****\n" );
PreOrderTraverse( bi, PrintElem );
printf( "\n" );
printf( "**PreOrderTraverse_stack**\n" );
PreOrderTraverse_stack( bi, PrintElem );
printf( "\n" );
printf( "*******BiTreeDepth********\n" );
depth = BiTreeDepth( bi );
printf( "the depth of the tree is %d\n", depth );
<span style="white-space:pre"> </span>printf( "*****PreDestroyBiTree*****\n" );
<span style="white-space:pre"> </span>PreDestroyBiTree( bi );
}