二叉树是非线性的,每个节点最多可以有两个后继。二叉树的存储结构有两种:顺序存储结构和链式存储结构。
1.顺序存储结构
二叉树的顺序存储就是用一位数组来存储二叉树中的节点,并且节点存储位置,也就是数组下标要能体现节点之间的逻辑关系,比如双亲之间与孩子的关系,左右兄弟的关系等。否则二叉树的基本操作在顺序存储结构上就难以实现。
依据二叉树的性质,完全二叉树和满二叉树采用顺序存储比较合适,因为树中的节点的序号可以唯一地反映节点之间的逻辑关系,这样既可以最大可能地节省存储空间,又可以利用数组元素的下标值确定节点在二叉树中的位置,以及节点之间的关系。如图(1)给的就是完全二叉树的顺序存储示意图。
对于一般的二叉树,如果仍然按照从上到下和从左到右的顺序将树中的节点顺序存储在一维数组中,则数组元素的下标之间的关系不能够反映二叉树中节点之间的逻辑关系,只有增添一些不存在的“虚节点”,使之成为一棵完全的二叉树的形式,再用一维数组顺序存储。如图(2)给出的就是一棵一般二叉树改造后的完全二叉树形态和其顺序存储状态示意图,图中的“/”表示不存在此节点,这种方法解决了一般二叉树的顺序存储问题。显然,对于这种需要增加许多“虚节点”才能完成改造一棵完全二叉树进行存储的一般二叉树,会造成空间的大量浪费,不适合使用顺序存储结构。
一种极端的情况是右单支树,如图(3)所示,一棵深度为k的右单支树,只有k个节点,却需要分配2的k次方 - 1个存储单元,这显然是对存储空间的浪费,所以顺序存储结构一般只用于完全二叉树。
不难发现,采用顺序存储结构,实际上是对非线性数据结构的线性化。
2.二叉树的链式存储结构
链式二叉树,就是用指针来指示元素的逻辑关系。通常有下面的两种形式:
(1.)二叉链表存储。二叉链表中,每个节点由三个域组成:
第一个用来存放二叉树节点的数据信息,即数据域(data);
第二个是左指针域,指向该节点左孩子的指针(存储地址);
第二个是右指针域,用来指向该节点右孩子的指针(存储地址);
注:当左孩子的指针或者右孩子不存在的时候,相应的指针阈值为空(用符号^或者NULL来进行表示)。
其链式存储的C语言结构体代码如下:
typedef struct node
{
datatype data;
struct node*lchild;
struct node*rchild;
}BinTNode, *BINTree;
lchild | data | rchild |
以上的表格就代表了二叉链表存储方式的节点结构;
此外,为了方便找到该节点的双亲节点,可以在二叉链表的基础上增加一个parent域
typedef struct node
{
datatype data;
struct node *lchild;
struct node *parent;
struct node *rchild;
}SNode,*BTree;
加上了parent的指针域之后,节点就变成了三叉链表节点的指针类型
思考:请画出三叉链表的存储结构
运用三叉链表来存储二叉树,既容易找到节点的左,右孩子节点,又容易找到其双亲节点。尽管在二叉链表当中无法由节点直接找到其双亲,但是由于二叉链表结构灵活,操作方便,对于一般的二叉树,甚者比顺序存储结构还节省空间。