常用的图的存储结构有邻接矩阵、邻接表、十字链表和邻接多重表。
- 图的数组表示法——邻接矩阵
图的逻辑结构分为顶点集合和边的集合两部分,一个顶点是一个元素,因此可以用一个一维数组存放顶点数据;一条边是用其关联的两个顶点表示的,即一个边元素有两个数据域,因此可以用一个矩阵存放顶点间的邻接关系(即边或弧),这个二维数组也被称为邻接矩阵。如果两个顶点是关联的,那么矩阵的值可以记为1,否则记为0。
邻接矩阵的定义
(1) 无向图邻接矩阵
(2)有向图邻接矩阵
(3)网的邻接矩阵
邻接矩阵数据结构描述
(1)结构描述
#define VERTEX_NUM 6 //图的顶点数
#define VERTEX_MAX_NUM 64 //图的最大顶点数
#define EDGE_MAX_NUM 20 //图的最大边数
typedef char VexType; //顶点的数据类型
typedef int InfoType; // 弧(边)的类型,如权值、边存在或不存在等
邻接矩阵 Adjacency Matrix——AM
typedef struct
{ VexType VertexArray[VERTEX_NUM]; //顶点数组
InfoType AdjMatrix [VERTEX_NUM][ VERTEX_NUM]; //邻接矩阵
} AM_Graph;
(2)邻接矩阵复杂度分析
2. 边集数组表示法
(1)边集数组的设计
边集数组只存储图中所有边(或弧)的信息,存储一条边的起点、终点(对于无向图,可选定边的任一端点为起点或终点)和边的相关信息(如权值),各边在数组中的次序可任意安排,也可根据具体要求而定。若需存储顶点信息,还需一个 VERTEX_NUM 个元素的一维数组。
(2)边集数组 (Edgeset Array)的数据结构描述
typedef struct //边集数组单元结构
{ VexType from_vex; //起点
VexType end_vex; //终点
InfoType weight; //权值项可以根据需要设置
} EdgeStruct;
EdgeStruct EdgeSet[EDGE_MAX_NUM]; //边集数组
(3)边集数组的复杂度分析
3. 图的链表表示法 1 ——邻接表
邻接矩阵排列直观容易理解,若图结构本身需要在解决问题的过程中动态地产生,则每增加或删除一个顶点都需要改变邻接矩阵的大小,这样做的效率显然是很低的。除此之外,邻接矩阵占用的存储单元数目只与图中顶点的个数有关,而与边(弧)的数目无关,对于边数相对顶点较少的稀疏图,这种存储结构空间浪费较大。
(1)邻接表的存储结构设计
对图的每个顶点建立一个单链表( n 个顶点建立 n 个单链表),第 i 个单链表中的结点包含顶点 Vi 的所有邻接结点。
对 n 个同类单链表并行管理,是采用“带行向量的链表表示方法”,这种存储结构称为邻接表。邻接表由头结点表和邻接结点链表两部分组成。
无向图邻接表
对于无向图来说,使用邻接表进行存储也会出现数据冗余,头结点 V1 所指链表中存在一个指向 V4 的邻接结点的同时,头结点 V4 所指链表也会存在一个指向 V1 的邻接结点。
有向图邻接表(出边表)
逆邻接表(入边表)
有时为了便于确定顶点的入度或以顶点为弧头的弧,可以建立一个有向图的逆邻接表。
带权邻接表
数据结构与邻接表基本一致,只在邻接结点中增加一个记录边的权值的数据域。
(2)邻接表数据结构描述
邻接表Adjacency List——AL
typedef struct AdjNode //邻接结点结构
{
int adjvex; //邻接点
AdjNode *next; // 邻接点指针
} AL_AdjNode;
typedef struct //邻接表顶点结点结构
{
VexType vertex; //顶点
AdjNode *link; // 邻接点头指针
} AL_VexNode;
typedef struct //总的邻接表结构
{
A_VexNode VexList[VERTEX_MAX_NUM]; //顶点表
int VexNum, ArcNum; //顶点数,弧(边)数
} AL_Graph;
建立邻接表
/*==========================================
函数功能:建立邻接表
函数输入:无
函数输出:无
共享数据:图的邻接矩阵
=============================================*/
void Create_AdjList()
{
AL_VexNode VexList[N]={0,NULL}; //顶点表
int j;
AL_AdjNode *Ptr,*nextPtr;
?
for(int i=0; i<N; i++)
{
VexList[i].vertex=i;
VexList[i].link=NULL;
j=0;
while(j<N)
{
if (AdjMatrix[i][j]!=0)//有邻接点
{
Ptr=(AL_AdjNode*)malloc(sizeof(AL_AdjNode));
Ptr->adjvex=j;
Ptr->next=NULL;
if (VexList[i].link==NULL)//首次加入邻接点
{
VexList[i].link=Ptr;
nextPtr=Ptr;
}
else
{
nextPtr->next=Ptr;
nextPtr=Ptr;
}
}
j++;
}
}
}
邻接表的空间复杂度分析
4. 十字链表
图的十字链表是有向图的另一种链式存储结构。该结构以入弧和出弧为线索,将有向图的邻接表和逆邻接表结合起来得到的,有顶点表和边表组成。十字链表结构也可以理解为将行的单链表和列的单链表结合起来存储稀疏矩阵,每个结点表示一个非零元素。
在十字链表中,对应于有向图中每一条弧都有一个弧结点,对应于每个顶点也有一个顶点结点。
十字链表复杂度分析
5. 邻接多重表
邻接多重表的存储结构和十字链表类似,也是由顶点表和边表组成,邻接多重表中,每一条边的信息用一个结点描述,一条边对应一个边结点。边结点中除了边关联的两个顶点 ivex,jvex 外,再加上与 ivex 连接的边结点位置、与jvex 连接的边结点位置。
//邻接多重表的顶点结构
typedef struct vnode
{ VexType vertex; //顶点信息
struct node *firstedge; //指向第一条依附于该顶点的边
} AML_VertexNode;
//邻接多重表的顶点表
AML_VertexNode G[VERTEX_NUM];
//邻接多重表的边结点结构
typedef struct node
{ int ivex, jvex; //边的两个关联点
struct node *ilink,*jlink; //分别指向依附于ivex和jvex的下一条边
} AML_EdgeNode;
邻接多重表复杂度分析
存储结构归结比较存储结构归结比较
- 关注顶点的存储结构
- 关注边的存储结构
- 图存储结构选择原则