二叉树的存储结构和遍历

二叉树的存储结构

 1.顺序存储结构

所谓二叉树的顺序存储,就是用一组连续的存储单元存放二叉树中的结点。一般是按照
二叉树结点从上至下、从左到右的顺序存储。这样结点在存储位置上的前驱后继关系并不一
定就是它们在逻辑上的邻接关系,然而只有通过一些方法确定某结点在逻辑上的前驱结点和
后继结点,这种存储才有意义。
依据二叉树的性质,完全二叉树和满二叉树采用顺序存储比较合适,树中结点的序号可
以唯一地反映出结点之间的逻辑关系,这样既能够最大可能地节省存储空间,又可以利用数
组元素的下标值确定结点在二叉树中的位置,以及结点之间的关系。
对于一般的二叉树,如果仍按从上至下和从左到右的顺序将树中的结点顺序存储在一维
数组中,则数组元素下标之间的关系不能够反映二叉树中结点之间的逻辑关系,只有增添一
些并不存在的空结点,使之成为一棵完全二叉树的形式,然后再用一维数组顺序存储。显然,
这种存储对于需增加许多空结点才能将一棵二叉树改造成为一棵完全二叉树的存储时,会造
成空间的大量浪费,不宜用顺序存储结构。最坏的情况是右单支树,一棵深度为 k 的右单支
树,只有 k 个结点,却需分配 2 k -1 个存储单元。
二叉树的顺序存储表示可描述为:
#define MAX_TREE_SIZE 100 //二叉树的最大结点数
typedef TElemType SqBiTree[MAX_TREE_SIZE] //0 号单元存放根结点
SqBiTree b; //将 b 定义为含有 MAX_TREE_SIZE 个 TElemType 类型元素的一维数组
2.链式存储结构
所谓二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示着元素的逻
辑关系。通常有下面两种形式。
(1)二叉链表
链表中每个结点由三个域组成,除了数据域外,还有两个指针域,分别用来给出该结点
左孩子和右孩子所在的链结点的存储地址。结点的存储的结构为:
其中,data 域存放某结点的数据信息;lchild 与 rchild 分别存放指向左孩子和右孩子的指
针,当左孩子或右孩子不存在时,相应指针域值为空。
二叉树的二叉链表存储表示可描述为:
typedef struct BiTNode{
TElemType data;
struct BiTNode *lchild;*rchild; //左右孩子指针
}BiTNode,*BiTree;
BiTree b;//即将 b 定义为指向二叉链表结点结构的指针类型。
(2)三叉链表
每个结点由四个域组成,结点的存储的结构为:
其中,data、lchild 以及 rchild 三个域的意义与二叉链表结构相同;parent 域为指向该结
点双亲结点的指针。这种存储结构既便于查找孩子结点,又便于查找双亲结点;但是,相对
于二叉链表存储结构而言,它增加了空间开销。
尽管在二叉链表中无法由结点直接找到其双亲,但由于二叉链表结构灵活,操作方便,
因此,二叉链表是最常用的二叉树存储方式。
3  二叉树的遍历
二叉树的遍历是指按照某种顺序访问二叉树中的每个结点,使每个结点被访问一次且仅
被访问一次。遍历是二叉树中经常要用到的一种操作。因为在实际应用问题中,常常需要按
一定顺序对二叉树中的每个结点逐个进行访问,查找具有某一特点的结点,然后对这些满足
条件的结点进行处理。通过一次完整的遍历,可使二叉树中结点信息由非线性排列变为某种
意义上的线性序列。也就是说,遍历操作实质是对一个非线性结构进行线性化操作。
1.先序遍历
先序遍历的递归过程为:若二叉树为空,遍历结束。否则,
(1)访问根结点;
(2)先序遍历根结点的左子树;
(3)先序遍历根结点的右子树。

void PreOrder(BiTree b){
if (b!=NULL){
Visit(b->data); //访问结点的数据域
PreOrder(b->lchild); //先序递归遍历 b 的左子树
lchild  data rchild
lchild  data  rchild parent
PreOrder(b->rchild); //先序递归遍历 b 的右子树
}
}



2.中序遍历
中序遍历的递归过程为:若二叉树为空,遍历结束。否则,
(1)中序遍历根结点的左子树;
(2)访问根结点;
(3)中序遍历根结点的右子树。
void InOrder(BiTree b){
if (b!=NULL)){
InOrder(b->lchild); //中序递归遍历 b 的左子树
Visit(b->data); //访问结点的数据域
InOrder(b->rchild); //中序递归遍历 b 的右子树
}
}


3.后序遍历
后序遍历的递归过程为:若二叉树为空,遍历结束。否则,
(1)后序遍历根结点的左子树;
(2)后序遍历根结点的右子树。
(3)访问根结点;

void PostOrder(BiTree b){
if (b!=NULL)){
PostOrder(b->lchild); //后序递归遍历 b 的左子树
PostOrder(b->rchild); //后序递归遍历 b 的右子树
Visit(b->data); //访问结点的数据域
}
}

4.层次遍历
所谓二叉树的层次遍历,是指从二叉树的第一层(根结点)开始,从上至下逐层遍历,
在同一层中,则按从左到右的顺序对结点逐个访问。
由层次遍历的定义可以推知,在进行层次遍历时,对一层结点访问完后,再按照它们的
访问次序对各个结点的左孩子和右孩子顺序访问,这样一层一层进行,先遇到的结点先访问,
这与队列的操作原则比较吻合。因此,在进行层次遍历时,可设置一个队列结构,遍历从二
叉树的根结点开始,首先将根结点指针入队列,然后从对头取出一个元素,每取一个元素,
执行下面两个操作:
(1)访问该元素所指结点;
(2)若该元素所指结点的左、右孩子结点非空,则将该元素所指结点的左孩子指针和右
孩子指针顺序入队。
此过程不断进行,当队列为空时,二叉树的层次遍历结束。
在下面的层次遍历算法中,二叉树以二叉链表存放,一维数组 Queue[MAX_TREE_SIZE]
用以实现队列,变量 front 和 rear 分别表示当前对队首元素和队尾元素在数组中的位置。

void LevelOrder(BiTree b){
BiTree Queue[MAX_TREE_SIZE];
int front,rear;
if (b==NULL) return;
front=-1;
rear=0;
Queue[rear]=b;
while(front!=rear) {
Visit(Queue[++front]->data); //访问队首结点数据域
if (Queue[front]->lchild!=NULL) //将队首结点的左孩子结点入队列
Queue[++rear]= Queue[front]->lchild;
if (Queue[front]->rchild!=NULL) //将队首结点的右孩子结点入队列
Queue[++rear]= Queue[front]->rchild;
}
}

5.二叉树遍历的非递归实现

前面给出的二叉树先序、中序和后序三种遍历算法都是递归算法。当给出二叉树的链式
存储结构以后,用具有递归功能的程序设计语言很方便就能实现上述算法。然而,并非所有
程序设计语言都允许递归;另一方面,递归程序虽然简洁,但执行效率不高。因此,就存在
如何把一个递归算法转化为非递归算法的问题。解决这个问题的方法可以通过对三种遍历方
法的实质过程的分析得到。
三种遍历路线正是从根结点开始沿左子树深入下去,当深入到最左端,无法再深入下去
时,则返回,再逐一进入刚才深入时遇到结点的右子树,再进行如此的深入和返回,直到最
后从根结点的右子树返回到根结点为止。先序遍历是在深入时(第一次经过)遇到结点就访
问,中序遍历是在从左子树返回时(第二次经过)遇到结点访问,后序遍历是在从右子树返
回时(第三次经过)遇到结点访问。
在这一过程中,返回结点的顺序与深入结点的顺序相反,即后深入先返回,正好符合栈
结构后进先出的特点。因此,可以用栈来帮助实现这一遍历路线。其过程如下:在沿左子树
深入时,深入一个结点入栈一个结点,若为先序遍历,则在入栈之前访问之;当沿左分支深
入不下去时,则返回,即从堆栈中弹出前面压入的结点,若为中序遍历,则此时访问该结点,
然后从该结点的右子树继续深入;若为后序遍历,则将此结点再次入栈,然后从该结点的右
子树继续深入,与前面类同,仍为深入一个结点入栈一个结点,深入不下去再返回,直到第
二次从栈里弹出该结点,才访问之。
(1)前序遍历的非递归实现
二叉树以二叉链表存放,一维数组 Stack[MAX_TREE_SIZE]用以实现栈,变量 top 用来
表示当前栈顶的位置。

void NRPreOrder(BiTree b){ //非递归先序遍历二叉树
BiTree Stack[MAX_TREE_SIZE],p;
int top=0;
if (b==NULL) return;
p=b;
while(!(p==NULL&&top==0)) {
while(p!=NULL) {
Visit(p->data); //访问结点的数据域
if(top< MAX_TREE_SIZE-1) //将当前指针 p 压栈
Stack[top++]=p;
else {
printf(“栈溢出”);
return;
}
p=p->lchild; //指针指向 p 的左孩子结点
}
if (top<=0) return; //栈空时结束
else {
p=Stack[- -top]; //从栈中弹出栈顶元素
p=p->rchild; //指针指向 p 的右孩子结点
}
}
}
(2)中序遍历的非递归实现
只需将先序遍历的非递归算法中的Visit(p->data)移到p=Stack[- -top]和p=p->rchild之间即
可。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值