主要内容 #
- 邻接矩阵存储
- 邻接表存储的C语言实现
- 举例说明
1. 邻接矩阵存储 #
使用图结构表示的数据元素之间虽然具有“多对多”的关系,但是同样可以采用用数组有效地存储图。
使用数组存储图时,需要使用两个数组,一个数组存放图中顶点本身的数据(一维数组),另外一个数组用于存储各顶点之间的关系(二维数组)。
存储图中各顶点本身数据,使用一维数组就足够了;存储顶点之间的关系时,要记录每个顶点和其它所有顶点之间的关系,所以需要使用二维数组。
不同类型的图,存储的方式略有不同,根据图有无权,可以将图划分为两大类:图和网 。
图,包括无向图和有向图;网,是指带权的图,包括无向网和有向网。存储方式的不同,指的是:在使用二维数组存储图中顶点之间的关系时,如果顶点之间存在边或弧,在相应位置用 1 表示,反之用 0 表示;如果使用二维数组存储网中顶点之间的关系,顶点之间如果有边或者弧的存在,在数组的相应位置存储其权值;反之用 0 表示。
结构代码表示:
#define MAX_VERtEX_NUM 20 //顶点的最大个数 #define VRType int //表示顶点之间的关系的变量类型 #define InfoType char //存储弧或者边额外信息的指针变量类型 #define VertexType int //图中顶点的数据类型 typedef enum{DG,DN,UDG,UDN}GraphKind; //枚举图的 4 种类型 typedef struct { VRType adj; //对于无权图,用 1 或 0 表示是否相邻;对于带权图,直接为权值。 InfoType * info; //弧或边额外含有的信息指针 }ArcCell,AdjMatrix[MAX_VERtEX_NUM][MAX_VERtEX_NUM]; typedef struct { VertexType vexs[MAX_VERtEX_NUM]; //存储图中顶点数据 AdjMatrix arcs; //二维数组,记录顶点之间的关系 int vexnum,arcnum; //记录图的顶点数和弧(边)数 GraphKind kind; //记录图的种类 }MGraph;
例如,存储上图中的无向图(B)时,除了存储图中各顶点本身具有的数据外,还需要使用二维数组存储任意两个顶点之间的关系。
由于 (B) 为无向图,各顶点没有权值,所以如果两顶点之间有关联,相应位置记为 1 ;反之记为 0 。构建的二维数组如下图所示。
在此二维数组中,每一行代表一个顶点,依次从 V1 到 V5 ,每一列也是如此。比如 arcs[0][1] = 1 ,表示 V1 和 V2 之间有边存在;而 arcs[0][2] = 0,说明 V1 和 V3 之间没有边。
对于无向图来说,二维数组构建的二阶矩阵,实际上是对称矩阵,在存储时就可以采用压缩存储的方式存储下三角或者上三角。
通过二阶矩阵,可以直观地判断出各个顶点的度,为该行(或该列)非 0 值的和。例如,第一行有两个 1,说明 V1 有两个边,所以度为 2。
存储图 1 中的有向图(A)时,对应的二维数组如下图所示:
例如,arcs[0][1] = 1 ,证明从 V1 到 V2 有弧存在。且通过二阶矩阵,可以很轻松得知各顶点的出度和入度,出度为该行非 0 值的和,入度为该列非 0 值的和。例如,V1 的出度为第一行两个 1 的和为 2 ; V1 的入度为第一列中 1 的和为 1 。所以 V1 的出度为 2 ,入度为 1 ,度为两者的和 3 。
2. 邻接表存储的C语言实现 #
#include <stdio.h> #define MAX_VERtEX_NUM 20 //顶点的最大个数 #define VRType int //表示顶点之间的关系的变量类型 #define InfoType char //存储弧或者边额外信息的指针变量类型 #define VertexType int //图中顶点的数据类型 typedef enum { DG, DN, UDG, UDN }GraphKind; //枚举图的 4 种类型 typedef struct { VRType adj; //对于无权图,用 1 或 0 表示是否相邻;对于带权图,直接为权值。 InfoType* info; //弧或边额外含有的信息指针 }ArcCell, AdjMatrix[MAX_VERtEX_NUM][MAX_VERtEX_NUM]; typedef struct { VertexType vexs[MAX_VERtEX_NUM]; //存储图中顶点数据 AdjMatrix arcs; //二维数组,记录顶点之间的关系 int vexnum, arcnum; //记录图的顶点数和弧(边)数 GraphKind kind; //记录图的种类 }MGraph; //根据顶点本身数据,判断出顶点在二维数组中的位置 int LocateVex(MGraph* G, VertexType v) { int i = 0; //遍历一维数组,找到变量v for (; i < G->vexnum; i++) { if (G->vexs[i] == v) { break; } } //如果找不到,输出提示语句,返回-1 if (i == G->vexnum) { printf("no such vertex.\n"); return -1; } return i; } //构造有向图 void CreateDG(MGraph* G) { int i, j; //输入图含有的顶点数和弧的个数 scanf("%d,%d", &(G->vexnum), &(G->arcnum)); //依次输入顶点本身的数据 for (i = 0; i < G->vexnum; i++) { scanf("%d", &(G->vexs[i])); } //初始化二维矩阵,全部归0,指针指向NULL for (i = 0; i < G->vexnum; i++) { for (j = 0; j < G->vexnum; j++) { G->arcs[i][j].adj = 0; G->arcs[i][j].info = NULL; } } //在二维数组中添加弧的数据 for (i = 0; i < G->arcnum; i++) { int v1, v2; int n, m; //输入弧头和弧尾 scanf("%d,%d", &v1, &v2); //确定顶点位置 n = LocateVex(G, v1); m = LocateVex(G, v2); //排除错误数据 if (m == -1 || n == -1) { printf("no this vertex\n"); return; } //将正确的弧的数据加入二维数组 G->arcs[n][m].adj = 1; } } //构造无向图 void CreateDN(MGraph* G) { int i, j; scanf("%d,%d", &(G->vexnum), &(G->arcnum)); for (i = 0; i < G->vexnum; i++) { scanf("%d", &(G->vexs[i])); } for (i = 0; i < G->vexnum; i++) { for (j = 0; j < G->vexnum; j++) { G->arcs[i][j].adj = 0; G->arcs[i][j].info = NULL; } } for (i = 0; i < G->arcnum; i++) { int v1, v2; int n, m; scanf("%d,%d", &v1, &v2); n = LocateVex(G, v1); m = LocateVex(G, v2); if (m == -1 || n == -1) { printf("no this vertex\n"); return; } G->arcs[n][m].adj = 1; G->arcs[m][n].adj = 1;//无向图的二阶矩阵沿主对角线对称 } } //构造有向网,和有向图不同的是二阶矩阵中存储的是权值。 void CreateUDG(MGraph* G) { int i, j; scanf("%d,%d", &(G->vexnum), &(G->arcnum)); for (i = 0; i < G->vexnum; i++) { scanf("%d", &(G->vexs[i])); } for (i = 0; i < G->vexnum; i++) { for (j = 0; j < G->vexnum; j++) { G->arcs[i][j].adj = 0; G->arcs[i][j].info = NULL; } } for (i = 0; i < G->arcnum; i++) { int v1, v2, w; int n, m; scanf("%d,%d,%d", &v1, &v2, &w); n = LocateVex(G, v1); m = LocateVex(G, v2); if (m == -1 || n == -1) { printf("no this vertex\n"); return; } G->arcs[n][m].adj = w; } } //构造无向网。和无向图唯一的区别就是二阶矩阵中存储的是权值 void CreateUDN(MGraph* G) { int i, j; scanf("%d,%d", &(G->vexnum), &(G->arcnum)); for (i = 0; i < G->vexnum; i++) { scanf("%d", &(G->vexs[i])); } for (i = 0; i < G->vexnum; i++) { for (j = 0; j < G->vexnum; j++) { G->arcs[i][j].adj = 0; G->arcs[i][j].info = NULL; } } for (i = 0; i < G->arcnum; i++) { int v1, v2, w; int m, n; scanf("%d,%d,%d", &v1, &v2, &w); m = LocateVex(G, v1); n = LocateVex(G, v2); if (m == -1 || n == -1) { printf("no this vertex\n"); return; } G->arcs[n][m].adj = w; G->arcs[m][n].adj = w;//矩阵对称 } } void CreateGraph(MGraph* G) { //选择图的类型 scanf("%d", &(G->kind)); //根据所选类型,调用不同的函数实现构造图的功能 switch (G->kind) { case DG: return CreateDG(G); break; case DN: return CreateDN(G); break; case UDG: return CreateUDG(G); break; case UDN: return CreateUDN(G); break; default: break; } } //输出函数 void PrintGrapth(MGraph G) { int i, j; for (i = 0; i < G.vexnum; i++) { for (j = 0; j < G.vexnum; j++) { printf("%d ", G.arcs[i][j].adj); } printf("\n"); } } int main() { MGraph G;//建立一个图的变量 CreateGraph(&G);//调用创建函数,传入地址参数 PrintGrapth(G);//输出图的二阶矩阵 return 0; }
3. 举例说明 #
例如,使用上述程序存储下图 4(a)的有向网时,存储的两个数组如下图 4(b)所示:
相应地运行结果为:
2 6,10 1 2 3 4 5 6 1,2,5 2,3,4 3,1,8 1,4,7 4,3,5 3,6,9 6,1,3 4,6,6 6,5,1 5,4,5 0 5 0 7 0 0 0 0 4 0 0 0 8 0 0 0 0 9 0 0 5 0 0 6 0 0 0 5 0 0 3 0 0 0 1 0