tips:本文只涉及图的存储结构
图的存储结构
1.邻接矩阵
1.1 定义
所谓邻接矩阵存储,是指用一个一位数组存储图中顶点的信息,用一个二位数组存储图中边的信息,存储顶点之间邻接关系的二维数组称为邻接矩阵。
结点数为n的图
G
=
(
V
,
E
)
G=(V,E)
G=(V,E)的邻接矩阵A是
n
∗
n
n*n
n∗n的。G的顶点编号为
v
1
,
v
2
,
.
.
.
,
v
n
v_1,v_2,...,v_n
v1,v2,...,vn。若
(
v
i
,
v
j
)
∈
E
(v_i,v_j)\isin E
(vi,vj)∈E,则
A
[
i
]
[
j
]
=
0
A[i][j]=0
A[i][j]=0。
A
[
i
]
[
j
]
=
{
1
,
若
(
v
i
,
v
j
)
或
<
v
i
,
v
j
>
是
E
(
G
)
中的边
0
,若
(
v
i
,
v
j
)
或
<
v
i
,
v
j
>
不是
E
(
G
)
中的边
A[i][j] = \begin{cases} 1, 若(v_i,v_j)或<v_i,v_j>是E(G)中的边\\ 0,若(v_i,v_j)或<v_i,v_j>不是E(G)中的边 \end{cases}
A[i][j]={1,若(vi,vj)或<vi,vj>是E(G)中的边0,若(vi,vj)或<vi,vj>不是E(G)中的边
对于带权图而言,若顶点
v
i
v_i
vi和
v
j
v_j
vj之间有边相连,则邻接矩阵中对应项存放着该边对应的权值,若顶点
v
i
v_i
vi和
v
j
v_j
vj不相连,则用
∞
\infin
∞来代表这两个顶点之间不存在边。
A
[
i
]
[
j
]
=
{
w
i
j
,
若
(
v
i
,
v
j
)
或
<
v
i
,
v
j
>
是
E
(
G
)
中的边
0
或
∞
,若
(
v
i
,
v
j
)
或
<
v
i
,
v
j
>
不是
E
(
G
)
中的边
A[i][j] = \begin{cases} w_{ij}, 若(v_i,v_j)或<v_i,v_j>是E(G)中的边\\ 0或\infin,若(v_i,v_j)或<v_i,v_j>不是E(G)中的边 \end{cases}
A[i][j]={wij,若(vi,vj)或<vi,vj>是E(G)中的边0或∞,若(vi,vj)或<vi,vj>不是E(G)中的边
1.2 实现代码
#define _CRT_SECURE_NO_WARNINGS 1
//图的存储结构:邻接矩阵(带权无向图)
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXVEX 100 //顶点数组的存储空间大小
#define GRAPH_INFINITY 65535
typedef int Status;
typedef int VertexType; //顶点的数据类型
typedef int EdgeType; // 带权图中 边上权值的数据类型
typedef struct
{
VertexType vexs[MAXVEX]; //顶点表 //从vexs[1]开始存储
EdgeType arc[MAXVEX][MAXVEX]; //边表 //arc[0][]、arc[][0]不存储信息
int numNodes; //顶点数
int numEdges; //边数
}MGraph;
//创建图
void CreateMGraph(MGraph* G)
{
int i, j, k, w;
printf("输入顶点数和边数:\n");
scanf("%d %d", &G->numNodes, &G->numEdges);
//读入顶点信息, 建立顶点表
for (i = 1; i <= G->numNodes; i++)
{
printf("请输入顶点信息:\n");
scanf("%d", & G->vexs[i]);
}
/* 邻接矩阵初始化 */
for (i = 1; i <= G->numNodes; i++)
{
for (j = 1; j <= G->numNodes; j++)
{
G->arc[i][j] = GRAPH_INFINITY; //初始化为 65535
}
}
/* 读入numEdges条边,建立邻接矩阵 */
for (k = 1; k <= G->numEdges; k++)
{
printf("输入边(vi,vj)上的下标i,下标j和权w:\n");
scanf("%d %d %d", &i, &j, &w); /* 输入边(vi,vj)上的权w */
G->arc[i][j] = w;
G->arc[j][i] = G->arc[i][j]; /* 因为是无向图,矩阵对称 */
}
}
int main()
{
MGraph G;
CreateMGraph(&G);
//打印顶点表信息
for (int i = 1; i <= G.numNodes; i++)
{
printf("%d ", G.vexs[i]);
}
printf("\n");
//打印边表信息
int i, j;
for (i = 1; i <= G.numNodes; i++)
{
for (j = 1; j <= G.numNodes; j++)
{
printf("%d\t", G.arc[i][j]);
}
printf("\n");
}
return 0;
}
2. 邻接表
2.1 定义
当一个图为稀疏图时,使用邻接矩阵法显然要浪费大量的存储空间,而图的邻接表法结合了顺序存储和链式存储的方法,大大减少了这种不必要的浪费。
所谓邻接表,是指对图G中的每个顶点
v
i
v_i
vi建立一个单链表,第i个单链表中的结点表示依附于顶点
v
i
v_i
vi的边(对于有向图则是以顶点
v
i
v_i
vi为尾的弧),这个单链表就称为顶点
v
i
v_i
vi的边表(对于有向图则为出边表)。
边表的头指针和顶点的数据信息采用顺序存储,所以在邻接表中存在两种结点:顶点表结点和边表结点。
顶点表结点有顶点域(data)和指向第一条邻接边的指针(firstedge)构成,边表结点由邻接点域(adjvex)和指向下一条邻接边的指针域(nextarc)构成。
对于带权值的网图,可以再边表结点定义中再增加一个weight的数据域,存储权值信息。
2.2 实现代码
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXVEX 100 //最大顶点数
typedef int Status;
typedef char VertexType; //顶点类型
typedef int EdgeType; //权值类型
typedef struct EdgeNode //边表结点
{
int adjvex; //邻接点域,存储该顶点对应的下标
EdgeType info; //用于存储权值,
struct EdgeNode* next; //链域,指向下一个邻接点
}EdgeNode;
typedef struct VertexNode //顶点表结点
{
VertexType data; //存储顶点信息
EdgeNode* firstedge; //边表头指针
}VertexNode,AdjList[MAXVEX];
typedef struct
{
AdjList adjList;
int numNodes;
int numEdges;
}GraphAdjList;
//建立图的邻接表结构
void CreateALGraph(GraphAdjList* G)
{
int i, j, k;
EdgeNode* e;
printf("输入顶点数和边数:\n");
scanf("%d %d", &G->numNodes, &G->numEdges);
//scanf("请按顺序输入顶点信息:\n");
for (i = 0; i < G->numNodes; i++)
{
scanf(&G->adjList[i].data);
G->adjList[i].firstedge = NULL;
}
for (k = 0; k < G->numEdges; k++) //头插法
{
printf("输入边(vi,vj)上的顶点序号:\n");
scanf("%d %d", &i, &j);
e = (EdgeNode*)malloc(sizeof(EdgeNode));
e->adjvex = j;
e->next = G->adjList[i].firstedge;
G->adjList[i].firstedge = e;
e = (EdgeNode*)malloc(sizeof(EdgeNode));
e->adjvex = i;
e->next = G->adjList[j].firstedge;
G->adjList[j].firstedge = e;
}
}
int main()
{
GraphAdjList G;
CreateALGraph(&G);
return 0;
}
3. 十字链表
3.1 定义
将邻接表和逆邻接表结合起来便是:十字链表。
逆邻接表一般指有向图的逆邻接表,即对每个顶点
v
i
v_i
vi都建立一个链接为
v
i
v_i
vi为弧头的表。
tips:十字链表是有向图的一种存储方法。
其结构如下:
-
顶点表结点结构
其中,firstin表示入边表头指针,指向该顶点的入边表中第一个结点;firstout表示出边表头指针,指向该顶点的出边表中的第一个结点。 -
边表结点结构
其中,tailvex是指弧起点在顶点表中的下标;headvex是指弧终点在顶点表中的下标;headlink是指入边表指针域,指向终点相同的下一条边;taillink是指边表指针域,指向起点相同的下一条边。如果是网,还可以增加一个weight域来存储权值。
4. 邻接多重表
4.1 定义
对无向邻接表的优化,同一条边在邻接表中用两个结点表示,而在邻接多重表中只有一个结点,从而提高对边的操作的便利性。
- 顶点表结点结构(和邻接表相同)
其中,data存储顶点信息,firstedge指向当前结点的第一条邻接边.
-
边结点结构
ivex和jvex是与某条边依附的两个顶点在顶点表中的下标;ilink指向依附顶点ivex的下一条边;jlink指向依附顶点jvex的下一条边。