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

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

需要完整代码可到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 == '.' ){  //如果输入为字符型的'.',表示该结点为空,将为该结点分配的空间释放,并返回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)若树为空,则返回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,表示树的深度为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)若树不为空:判断树是否为叶子结点
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,非空返回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 );
}
 
 







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值