链式二叉树的前序创建、递归前序遍历、非递归堆栈前序遍历、前序销毁以及求二叉树的深度

来源:互联网 发布:nginx下载 编辑:程序博客网 时间:2024/06/01 21:24

链式二叉树的前序创建、递归前序遍历、非递归堆栈前序遍历、前序销毁以及求二叉树的深度

需要完整代码可到CSDN资源页搜索链式二叉树的前序创建、递归遍历、利用堆栈的非递归遍历、前序销毁以及求二叉树的深度进行下载

1、数据结构

二叉树的一个结点的数据结构包含4个域:
(1)值域:保存结点的值,类型为ElemType,可以自己定义为合适的类型,为了简单,我们再次设定值域的类型为char
(2)指向父结点的指针
(3)指向左儿子结点的指针
(4)指向右儿子结点的指针
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,
(2)输入d的左儿子c,
(3)输入c的左儿子b,
(4)输入b的左儿子a,a为叶子结点,故输入a完毕之后紧接着输入两个空结点'.'以标志a为叶子结点,此时b的左子树输入完毕
(5)输入b的右子树,b的右子树为空,故应输入一个空结点'.'来标志b的右子树为空,此时c的左子树输入完毕
(6)输入c的右子树,c的右子树为空,故应输入一个空结点'.'来标志c的右子树为空,此时d的左子树输入完毕
(7)输入d的右子树,输入d的右儿子e,e的左子树为空,故输入e完毕之后紧接着输入一个空结点'.'来标识e的左子树为空,此时e的左子树输入完毕
(8)输入e的右子树,输入e的右儿子f,f的左子树为空,故输入f完毕之后紧接着输入一个空结点'.'来标识f的左子树为空,此时f的左子树输入完毕
(9)输入f的右子树,输入f的右儿子g,g为叶子结点,故输入g完毕之后紧接着输入两个空结点'.'以标志f为叶子结点,此时f的右子树输入完毕
整棵树的结点页输入完毕,输入结点的顺序为:dcba....e.f.g..
(1)前两个'.'标志a为叶子结点
(2)第三个'.'标志b的右子树为空
(3)第四个'.'标志c的右子树为空
(4)第五个'.'标志e的左子树为空
(5)第六个'.'标志f的左子树为空
(6)最后两个'.'标志g为叶子结点
//按先序次序输入二叉树中结点的值(一个字符),'.'表示空树//对于先序输入,每个叶子结点的左右结点都先输入'.',即在输入一个叶子结点后,要连续输入两个'.'程序才能认定这个结点为叶子结点BiTree *PreCreateBiTree(  ) {BiTree *tree;tree = NodeMalloc(  );  //为该结点分配空间printf( "请输入结点的值:\n" );scanf( "%s", &(tree->data) );  //输入结点的值if( tree->data == '.' ){  //如果输入为字符型的'.',表示该结点为空,将为该结点分配的空间释放,并返回NULLfree( 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)若树为空,则返回0
(2)若树不为空:则树的深度为其左右子树中深度值较大的值加1

1)计算左子树的深度depth_l,递归调用相当于又从(1)开始

2)计算右子树的深度depth_r,递归调用相当于又从(1)开始

3)比较两个深度值的大小,返回较大值加1为当前树的深度

//判断树的深度int BiTreeDepth( BiTree *tree ) {int depth_r;int depth_l;if( tree == NULL )  //如果为空树,则返回0,表示树的深度为0return 0;else{  //否则计算当前树的左右子树的深度depth_l = BiTreeDepth( tree->leftchild );depth_r = BiTreeDepth( tree->rightchild );if( depth_l > depth_r )  //当前树的深度为其左右子树深度较大的值加1return depth_l + 1;elsereturn depth_r + 1;}}

6、先序顺序销毁一个树

利用递归:
(1)若树为空,打印树为空的信息
(2)若树不为空:判断树是否为叶子结点
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、为了完整性在此给出其他相关数据结构和函数

(1)堆栈函数stack.c
#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,非空返回FALSEint 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 ); //弹栈,并将出栈元素的值赋给eint Length( Lstack *S );//栈中元素个数int StackEmpty( Lstack *S );//判断栈是否为空,若为空,返回TRUE,非空返回FALSEvoid 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;   //初始化结点的各个指针域为NULLnode->rightchild = NULL;node->parent = NULL;return node;}//按先序次序输入二叉树中结点的值(一个字符),'.'表示空树//对于先序输入,每个叶子结点的左右结点都先输入'.',即在输入一个叶子结点后,要连续输入两个'.'程序才能认定这个结点为叶子结点BiTree *PreCreateBiTree(  ) {BiTree *tree;tree = NodeMalloc(  );  //为该结点分配空间printf( "请输入结点的值:\n" );scanf( "%s", &(tree->data) );  //输入结点的值if( tree->data == '.' ){  //如果输入为字符型的'.',表示该结点为空,将为该结点分配的空间释放,并返回NULLfree( 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;elsereturn FALSE;}//判断树的深度int BiTreeDepth( BiTree *tree ) {int depth_r;int depth_l;if( tree == NULL )  //如果为空树,则返回0,表示树的深度为0return 0;else{  //否则计算当前树的左右子树的深度depth_l = BiTreeDepth( tree->leftchild );depth_r = BiTreeDepth( tree->rightchild );if( depth_l > depth_r )  //当前树的深度为其左右子树深度较大的值加1return depth_l + 1;elsereturn 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 );
}








0 0