二叉树的顺序存储:
用一块连续的空间存储二叉树的节点,存储的顺序按从上到下,从左至右的顺序。
顺序存储一般适合存储 : 满二叉树和完全二叉树。
对于一般二叉树,如果用顺序存储,且需要反映出一定的逻辑关系(即能用数组元素下标值反映元素在二叉树中的位置),此时可能会需要定义一些虚结点。即存储时,存储虚结点的空间即存储0值。(弊端是浪费了太多空间)所以,对于一般二叉树不适合用顺序存储结构。
二叉树的链式存储: 用链表来表示一棵二叉树
1二叉链表存储结构: 链表每个结点包含两个指针域(分别指向 左孩子结点和右孩子结点)以及数据域。 ,当左孩子或右孩子不存在时将对应的指针域设为NULL。
2三叉链表存储结构:链表每个结点包含三个指针域(分别指向左孩子结点和右孩子结点以及双亲结点)以及数据域。
二叉树是最常用二叉树存储结构(对于一般的二叉树,二叉链表存储结构甚至比顺序存储结构还要节省存储空间)。
含有n个结点的二叉链表中含有n+1个空指针域。 设深度为k , 则最后一层的结点数为 2^(k-1) , 总结点数为 2^k-1 ,由于每个结点含有两个指针域,故有2*2^(k-1)+1个空指针域,即 为 n+1 此为特殊情况。
正常推导为:由于n个结点有2n个指针域,但是只需要存储n-1个结点的信息,所以有n+1个指针域会空闲。
二叉树的静态链表存储结构,和静态链表一样是为了那些不支持指针类型的程序设计语言。
二叉树的正则化扩展:当结点存在一个子结点,但无另外的一个左或右孩子结点时,补充一个虚结点,使原二叉树的结点都称为分支结点,这种补充称为二叉树的正则化扩展。
二叉树的遍历算法:递归过程。
先序遍历: 1、先访问根结点 ;2、先序遍历根结点的左子树;3、先序遍历根结点的右子树。
中序遍历: 1、中序遍历根结点的左子树;2、访问根结点;3、中序遍历根结点的右子树。
后序遍历: 1、后序遍历根结点的左子树;2、后序遍历根结点的右子树;3、访问根结点。
/
二叉树的非递归遍历算法:
先序遍历 : 实质上就是进栈出栈的过程。
void Preorder(BiTree BT , *visit( ElemType))
{
if (BT)
{InitStack(S) ; //初始化一个栈
push(S,BT) ;
while(!StackEmpty(S))
{ pop(S,p) ; visit(p->data) ; //访问根结点 if(p->rchild) push(S,p); if(p->lchild) push(S,p) ;} //栈是先进后出,所以先进栈右孩子结点。
}
}
///
中序遍历:同理
void Inorder(BiTree BT,*visit(ElemType))
{
if(BT)
{p = BT;
initStack(S) ;
while( p || !Stackempty(S))
{ if(p) { push(S,p) ; p=p->lchild ; //深入到左子树的叶子结点。}
else { pop(S,p); visit(p->data); p=p->rchild ; //进入(左子树往下的分支)右子树}
}
}
}
///
后序遍历:
Postorder(BiTree BT ,*visit(ElemType))
{
BitTree S[n] ; //初始化一个静态栈(用数组表示栈,top为栈顶索引)
int Tag[n] ; top =0 ;
p = BT ;
while( p || top )
{
while( p )
{ S[top] = p ; Tag[top] =0 ; top++ ; }
while( top && Tag[top]==1)
{ p = S[top] ; top-- ; visit(p->data) ;}
if(top)
{ Tag[top] = 1 ; p=S[top]->rchild ; } //因为已经到了左子树的叶子结点,此时p指向叶子结点的左孩子结点,且其指针为NULL,故此时应该访问栈里根结点的右子树结点。
}
}
///
//
层次遍历:从上到下,每层从左至右访问结点。 先遇到的先访问,其符合队列的特性。
算法描述:
Levelorder(BiTree BT ,*visit(ElemType))
{
InitQueue(Q) ;
EnQueue(Q,BT) ;
while(!QueueEmpty(Q))
{
DeQueue(Q,p) ;
visit(p->data) ;
if (p->lchild) EnQueue(Q,p->lchild) ;
if(p->rchild) EnQueue(Q,p->rchild) ;
}
}