数据结构 第四章 树——二叉树的存储结构与遍历(C语言)

二叉树的顺序存储(数组):

#include <stdio.h>
#include <stdlib.h>

#define ElementType int
#define Maxsize 10
 
typedef struct{
	ElementType data[ Maxsize + 1 ];    //下标为0的位置记录树中元素的个数 
	int tag_parent;
}BinTree;

typedef BinTree* BT;

int IsEmpty( BT ptrt );                 //判断二叉树是否为空 
void CreateBinTree();                     //创建一颗空树 
void Traversal( BT ptrt );              //按某种次序遍历一棵树 

判空:

int IsEmpty( BT ptrt )
{
	return ptrt -> data[0] == 0;
}

创建一颗空树:

void CreateBinTree( BT ptrt )
{
	ptrt -> data[0] = 0;
}

二叉树的链式存储:

#include <stdio.h>
#include <stdlib.h>

#define ElementType int
 
typedef struct BTree{
	ElementType data;    
	struct BTree *Left, *Right;
}BinTree;

typedef BinTree* BT;

判空:

int IsEmpty( BT ptrt )
{
	return ( ptrt -> Left == NULL && ptrt -> Right == NULL );
}

创建一颗空树:

BT CreateBinTree()
{
	BT ptrt = ( BT )malloc( sizeof( BinTree ) );
	if( ptrt == NULL ){
		printf( "请求空间出错\n" );
		return NULL;
	}
	ptrt -> Left = Ptrt -> Right = NULL;
	return ptrt;
}

二叉树的遍历:即按某条线路访问树中的结点,每个节点被访问一次,且只访问一次,常见的遍历算法分为先序(NLR)、中序(LNR)、后序(LRN)和层次遍历。(以链式存储为例)

先序遍历(根左右):

void PreOrder( BT ptrt )        //先序遍历 
{
	if( ptrt ){
		visit( ptrt );
		PreOrder( ptrt -> Left );
		PreOrder( ptrt -> Right );
	}
} 

中序遍历(左根右):

void InOrder( BT ptrt )        //中序遍历 
{
	if( ptrt ){
		InOrder( ptrt -> Left );
		visit( ptrt );
		InOrder( ptrt -> Right );
	}
} 

后序遍历(左右根):

void PostOrder( BT ptrt )     //后序遍历 
{
	if( ptrt ){
		PostOrder( ptrt -> Left );
		PostOrder( ptrt -> Right );
		visit( ptrt );
	}
}

先序、中序、后序遍历过程中经过结点的路线一样,只是访问各结点的时机不同。

层次遍历(需要借助一个队列):

void LevelOrder( BT ptrt )            //层次遍历
{
	InitQueue( Q );
	BT p;
	EnQueue( Q, ptrt );
	while( !IsEmpty( Q ) ){
		DeQueue( Q , p );
		visit( p );
		if( ptrt -> Left )
			EnQueue( ptrt -> Left );
		if( ptrt -> Right )
			EnQueue( ptrt -> Right );
	}
}

层次遍历的基本过程:
(1)根节点入队;
(2)从队列中取出一个元素;
(3)访问该节点;
(4)如果该结点的左孩子或右孩子非空,就让结点的左孩子、右孩子入队。

二叉树遍历的非递归实现(借助堆栈):
先序遍历:

void PreOrder_( BT ptrt )
{
	InitStack( S );
	BT p = ptrt;
	while( p || !IsEmpty( S ) ){
		while( p ){
			push( S, p );
			visit( p );
			p = p -> Left;
		}
		if( !IsEmpty( S ) ){
			p = pop( S );
			p = p -> Right;
		}
	}
}

中序遍历:

void InOrder( BT ptrt )
{
	InitStack( S );
	BT p = ptrt;
	while( p || !IsEmpty( S ) ){
		while( p ){
			push( S, p );
			p = p -> Left;
		}
		if( !IsEmpty( S ) ){
			p = pop( S );
			visit( p );
			p = p -> Right;
		}
	}
}

后序遍历(正确性还有待考验,等之后写出了完整的树程序之后再检验修改):

void PostOrder_( BT ptrt ) 
{
	InitStack_( S );
	BT p = ptrt;
	while( ptrt || !IsEmpty_( S ) ){
		while( ptrt ){
			push_( S, p );
			S -> tag++;                  
			p = p -> Left;
		}
		if( !IsEmpty_( S ) ){
			if( p -> tag != 2 ){
				Pop_( S, p );
				p = p -> Right;
				Push_( S, p );
				S -> tag++;
			}
			else{
				Pop_( S, p );
				visit( p );
			}
		} 
	}
}

后序遍历所用的栈中除了包含数据元素之外,还要包含一个标志域,标志域表征这是第几次访问到这个元素,初始值均设为0,当第一次遇到这个元素时,把这个元素入栈,tag++,之后第二次遇见它时,把元素出栈然后转向它的右子树后再次把它入栈,tag++,等第三次再遇到它的时候(判断tag是否等于2),然后访问它。

由不同的遍历序列可以确定一颗二叉树:
(1)知道先序和中序可以确定一颗二叉树;
(2)直到中序和后序可以确定一颗二叉树;
(3)知道层序和中序可以确定一颗二叉树;
(4)知道先序和后序不能确定一颗二叉树。

二叉树遍历的核心问题:如何把二维结构线性化,所以需要一个存储结构保存暂时不访问的结点:
(1)借助堆栈;
(2)借助队列。

猜你喜欢

转载自blog.csdn.net/qq_40344308/article/details/88907742