1. 二叉树的顺序存储结构
图1-完全二叉树
二叉树的顺序存储结构是按照编号次序存储节点,即对树中每个节点进行编号,其编号从小到大的顺序就是节点在连续存储单元的先后次序
。
图2- 二叉树的顺序存储结构
当我们采用顺序存储结构去存储一个节点时可以根据二叉树的性质去求该节点的左孩子和右孩子节点,以及双亲节点:
1. 若编号为i的节点的左孩子节点的编号为2i;右孩子节点的编号为(2i+1)。
2. 除树根节点外,若一个节点的编号为i,则它的双亲节点的编号为[ i/2 ]。
2. 存储一般的二叉树
图3-一般二叉树
上面讲的是完全二叉树的存储,但是在应用中完全二叉树是一个特例,更多的是考虑一般的二叉树的存储结构。而对于一般二叉树的结构先采用空节点补全成为完全二叉树,但是这并不是说我们要真的补全,补全的目的只是为了方便编号,确定存储结构,但是我们并不关心补全的节点
。
当把一般二叉树进行补全为完全二叉树后,再对节点编号,最后确定了存储结构,下面给出一般二叉树补全后的存储结构:
图4-一般二叉树的存储结构
在图4中凡是#符号代替的都是补全的节点,且下标为0的位置一般都不存储任何有效数据
。对于编号为2的节点来说,它的左孩子节点为空,所以用编号为4的节点进行补全。对于编号为3的节点来说,它的左孩子节点也是为空,因此用编号为6的节点进行补全,其他补全的节点以此类推……
对于这样的存储结构,我们用程序设计语言来定义存储结构,如下所示:
#define MaxSize 15
typedef ElemType SqBTree[MaxSize];
SqBTree bt="#ABD#C#E######F";
bt[i]的子女节点:bt[2*i] , bt[2*i+1]
bt[i]的双亲节点:bt[[ i/2 ] ]
不知道大家是否发现没,其实这样的存储结构有一个问题,那就是真正存储节点数据的存储单元并不多,出现了大量的浪费,而图4中至少有一半以上的存储空间没有存储数据,对于存储空间的利用率严重不足,因此顺序存储结构适用于完全二叉树,并不适用于一般的二叉树,而对于一般的二叉树来说,有更好的存储方案,有小伙伴猜到了,没错,就是接下来要介绍的链式存储结构
。
3. 二叉树的链式存储结构
图5
在工程应用中,对于二叉树的存储结构更多的是采用链式存储结构,而二叉树的链式存储结构中,对于每个节点我们不要要存储节点本身,还要存储该节点的左孩子节点和右孩子节点,因此这里我们直接使用程序设计语言来定义二叉树的链式存储结构,如下所示:
typedef struct node
{
ElemType data; //数据域
struct node *lchild,*rchild; //lchild记录左孩子,rchild记录右孩子
} BTNode;
在这样的存储结构中我们发现,data表示值域,用于存储对应的节点数据元素,lchild和rchild分别表示左指针域和右指针域,用于分别存储左孩子节点和右孩子节点(即左、右子树的根节点)的存储位置。
那么图5中的二叉树对应的存储结构就是如图6所示,而后面我们要针对二叉树进行一些基本运算也是基于这样的链式存储结构:
图6-二叉树的链式存储结构
我们发现二叉树的链式存储结构弥补了顺序存储结构的一些缺点:不需要考虑补齐节点的问题,也不会出现存储空间浪费的问题,无论是完全二叉树,满二叉树,还是一般二叉树都适用于这种存储方案。
4. 总结
到这里,对于树结构的一些基本概念,基本性质,术语等总算是告一段落了,这些内容比较繁杂,需要花一些时间理解,总结。现在我们暂且枕戈待旦,之后再开启新的篇章。