二叉树的顺序存储(数组):
#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)借助队列。