数据有有线性结构、树形结构、图状结构和集合四种逻辑结构,那么它们是如何存储的呢?
数据结构的存储结构有两种,分别是顺序存储和链式存储。顺序存储的特点是借助元素在存储器中的相对位置来表示数据元素之间的逻辑关系;链式存储的特点是借助指针表示数据元素质检单逻辑关系。
1.线性结构:结构中的元素之间存在着一对一的线性关系。
如图为一个线性结构,那么它的顺序存储和链式存储如何呢?如下图:
顺序结构 链式结构
线性结构如数组的存法,按一定顺序存放;而链式结构如链表的存法,结点可以任意存放,如上图,所以要用next相连,以保证每一个结点都有唯一确定的前驱和后继。
2.树形结构:结构中的数据元素之间存在着一对一的线性关系。(这里我们以二叉树为例)
如图是一个简单的二叉树,其中6号结点不存在,其顺序存储如下图:
因为是二叉树,所以我们可以认为每个结点都有两个“孩子”,不存在的可以用“空”来表示(上图中的6号就是空),这样就可以用一行表示一个完整的二叉树,并且知道每一个结点所对应的前驱和后继。
至此我们就可以发现:
2,3对应的前驱是1;
4,5对应的前驱是2;
6,7对应的前驱是3。
这样我们就可以推导出几个关系:
若结点为i,则i的前驱是i/2;i的左后继是2i;i的右后继是2i+1。
下面再给大家一个例子供参考(其中符号∧代表空,也可用null表示):
那么二叉树的链式结构又如何呢?
二叉树的结点结构如下图:
LChild域指向该结点的左孩子,Data域记录该结点的数据,RChild域指向该结点的右孩子。二叉树的链式结构存储如下图所示:
我们可以让头指针指向A,则二叉链表结点结构的描述如下:
typedef struct Node { DataType date; Struct Node *Lchild; Struct Node *Rchild; }BiTNode, *BiTree;为了方便访问某结点的双亲,还可以给链表结点增加一个双亲字段parent,用来指向其双亲结点。每个结点由四个域组成,其结点结构为:
在实际应用中,要根据二叉树的形态和具体要进行的操作来选择决定采用哪种存储结构。
3.图状结构:结构中的数据元素之间存在着多对多的任意关系。
以无向图为例,如下图所示:
则可得出关系为:
A: (A,B) (A,C)
B:(B,A) (B,C) (B,D)
C:(C,A) (C,B) (C,D)
D: (D,B) (D,C)
以数组的存储方式(邻接矩阵)进行存储可得:
其中“1”代表存在关系,“0”代表不存在关系。
将其转化为链式的形式如下图:
对其链式结构我们可采用指针数组(邻接表)的方式来连接单链表,其中的字母应换成相应的数组下标,如下图:
邻接矩阵是不错的一种图存储结构,但是,对于边数相对顶点较少的图,这种结构存在对存储空间的极大浪费。因此,找到一种数组与链表相结合的存储方法称为邻接表。图中每个顶点的所有邻接点构成一个线性表,由于邻接点的个数不定,所以,用单链表存储,无向图称为顶点的边表,有向图则称为顶点作为弧尾的出边表。
有向图情况类似,大家可以自己试一试。而在有向图中,对于带权值的网图,可以在边表结点定义中再增加一个数据域,存储权值信息即可。如下图: