注意:文中彩色代码均在Visual Studio 2022编译器中编写,本文为C语言数据结构手抄版,文中有部分改动,非原创。
目录
第六章 图
学习目标
- 熟悉图的有关术语和概念。
- 熟练掌握图的两种存储结构,并能根据问题的要求选择合适的存储结构。
- 熟练掌握图的深度优先和广度优先遍历算法。
- 理解生成树和最小生成树的概念,理解Prim和Kruskal算法的基本思想,并能够对给定的连通图,构造最小生成树。
- 理解和掌握求最短路径的Djkstra算法的基本思想,并能根据给定的有向图画出求单源最短路径的过程示意图。
- 理解拓扑排序的基本思想和步骤,能够对给定的有向图,写出图的拓扑排序序列。
6.1.图的定义和基本术语
图(Graph)是一种复杂的非线性结构。在线性结构中,数据元素之间满足唯一的线性关系,每个数据元素(除第一个和最后一个外)只有一个直接前趋和一个直接后继;在树形结构中,数据元素之间有着明显的层次关系,并且每个元素只与上一层中一个元素(双亲结点)及下一层中多个元素(孩子结点)相关;而在图形结构中,结点之间的关系可以是任意的,图中任意两个元素之间都可能相关。因此,图比线性表和树形结构更为复杂。图形结构在人工智能、工程、数学、物理、化学、计算机科学等领域中都有着广泛的应用。
图形结构简称为图。图G由两个集合V和E组成,定义为G=(V, E),其中V是顶点的有限非空集合, E是由V中顶点偶对表示的边的集合。通常,V(G)和E (G)分别表示图G的顶点集合和边集合。E(G)也可以为空集,即图G只有顶点而没有边。
对于一个图G,若每条边都是有方向的,则称该图为有向图。在有向图中,一条有向边是由两个顶点组成的有序对,通常用尖括号表示。例如,<vi,vj>就表示一条有向边,此边称为顶点vi的一条出边,顶点vj的一条入边;另外,称vi为起始端点(或起点),vj为终止端点(或终点)。因此,<vi, vj>和<yj, vi>是两条不同的有向边。有向边又称为弧,边的起点称为弧尾,边的终点称为弧头。例如,图6.1 (a)中所示的图G1是一个有向图,该图的顶点集和边集分别为
V(G1)={v1,v2,v3}
E(G1)={<V1,V2>,<V2,V3>,<V3,V1>,<V1,V3>}
对于一个图G,若每条边都是没有方向的,则称该图为无向图。在一个无向图中,边均是顶点的无序对,通常用圆括号表示。因此, (vi, vy)和(vy, vi)表示同一条边。图6.1 (b)中所示的G2就是一个无向图,此图的顶点集和边集分别为:
V(G2)={v1,v2,v3,v4,v5}
E(G2)={(v1,v2),(v1,v4),(v2,v3),(v2,v5),(v3,v4),(v3,v5),(v4,v5)}
在无向图中,若存在一条边(vi, vy),则称顶点vi,vy为该边的两个端点,并称它们互为邻接点,或称vi和vy相邻接。在有向图中,若<vi, vy>是一条边,则称顶点vi邻接到vy,顶点vj邻接于顶点Vi。若存在边(vi, vy)或弧<vi, vy>,则称该边或弧关联(依附)于vi,和vy。
我们通常用n表示图中的顶点数,用e表示图中边或弧的数目,并且在下面的讨论中不考虑顶点到自身的边,即若(vi, vy)或<vi, vy>是E(G)的一条边,则要求vi≠vy因此,对于无向图,e的取值范围是0~n(n-1)/2。我们将具有n(n-1)/2条边的无向图称为无向完全图。同理,对于有向图,e的取值范围是0~n(n-1),称具有n(n-1)条边或弧的有向图为有向完全图。
在无向图中,顶点v的度定义为以该顶点为一个端点的边的数目,记为D(v)。对于有向图G,顶点v的度分为入度ID (v)和出度OD (V),入度是以该顶点为终点的入边数目,出度是以该顶点为起点的出边数目,该顶点的度等于其入度和出度之和。
对于一个图G任意两个点都有一条边相连称为完全图。
例如,图G2中顶点v3的度D (v3) =3;图G1中顶点v1的入度ID (v1)=1,出度OD (v1) =2,所以D(v1) =ID (v1) +OD (v1) =1+2=3。
一般地,如果顶点vi,的度为D(vi),那么,不管是有向图或是无向图,顶点数n,边数e和度数之间有如下关系:
例如,图G1的边数e=(D (v1)+D (v2) +D (v3)) /2=(3+2+3)/2=4。
设G=<V, E>, G'=<V', E'>为两个图(同为无向图或有向图),若V'⊆V, E'⊆E,且E'中的边所关联的结点都在V'中,则称G'是G的子图,记作G'⊆G。例如下图b、c是a的子图。
例如,图6.2 (a)中所示就是图6.1(a)中所示图G1的子图;图6.2(b)中所示就是图6.1(b)中所示图G2的子图。
在无向图G中,若存在一个顶点序列vp, vi1, vi2,···, vim, vq,使得(vp, vi1), ( vi1, vi2), ···, ( vim, vq)均属于E (G),则称顶点vp到vq存在一条路径。如果G是有向图,则路径也是有向的。路径长度是指一条路径上经过的边的数目。若一条路径上除了起点和终点可以为同一个顶点外,其余顶点均不相同的路径称为一条简单路径。若一条简单路径上的起点和终点为同一个顶点,则称该路径为回路或环。除了路径的起点和终点相同外,其余顶点均不相同的路径称为简单回路(简单环)。
在无向图G中,如果从顶点vi到顶点vj有路径,则称vi和vj是连通的。若图G中任意两个顶点vi和vj都连通,则称G为连通图,如图6.1(b)所示的图G2就是一个连通图,图6.3 (a)中的图G3则是非连通图。
无向图的极大连通子图称为连通分量。显然,任何连通图的连通分量只有一个,即其自身。而非连通的无向图有多个连通分量。极大连通子图指的是子图G1是图G的连通子图,将图G的任何不在子图G1中的顶点加入该子图G1中,子图G1不再连通。
例如,图G3有三个连通分量,如图6.3 (b)所示。
在有向图G中,如果对任意两个顶点vi和vj都连通,即从vi到vj和从vj到vi都存在路径,则称图G是强连通图。
有向图中的极大连通子图称作有向图的强连通分量。极大强连通子图指的是子图G1是图G的强连通子图,将图G的任何不在子图G1中的顶点加入该子图G1中,子图G1不再是强连通的。
如图6.4 (a)中的G4有三个强连通分量,分别对应图6.4中的图(b)、(c)和(d)。
两点之间相互连通才能称为强连通,从A到B,从B到A任意一条不满足不能叫强连通。 |
若在一个图的每条边上标上某种数值,表明从一个顶点到另一个顶点的距离或耗费,该数值称为该边的权。边上带权的图称为带权图,带权的图称为网,带权的连通图称为网络,其权值往往是具有某种意义的数。例如,它们可以表示两个顶点之间的距离、耗资等。图6.5中的图G5就是一个带权图的例子。
6.2.图的存储结构
图的存储结构又称图的存储表示。图的存储表示方法很多,这里主要介绍两种最常用的方法,即邻接矩阵和邻接表表示法。为了适应C语言的描述,从本节起,假定图的顶点序号从0开始,即图G的顶点集V(G) ={V0, V1, ···, Vn-1}。
6.2.1.邻接矩阵
邻接矩阵是表示图形中顶点之间相邻关系的矩阵。设G=(V, E)是具有n个顶点的图,则G的邻接矩阵是具有如下定义的n阶方阵:
例如,图6.6中无向图G6和有向图G7的邻接矩阵分别如图6.7中的A1和A2所示。由A1可以看出,无向图的邻接矩阵是按主对角线对称的。
若G是一个带权图(网图),则用邻接矩阵表示也很方便,只要把1换为相应边上的权值,0的位置上可以不动或将其换成无穷大∞表示。
这里的wij表示(vi,vj)上的权值。无穷大表示一个计算机允许的、大于所有边上权值的值,也就是一个不可能的极限值。
例如,如图6.5所示的带权图的邻接矩阵如图6.8中的A3所示。
对于有向图,可以有两个邻接矩阵,一个表示出边,一个表示入边。图的邻接矩阵表示,除了需要用一个二维数组存储顶点之间相邻关系的邻接矩阵外,通常还需要使用一个具有n个元素的一维数组来存储顶点信息,其中下标为i的元素存储顶点vi的信息。因此,图的邻接矩阵表示的存储结构定义如下:
#define MaxVertex 50 // 最大定点数 struct Graph { char* vertex[MaxVertex][8]; // 顶点数组 假定为char类型 int edge[MaxVertex][MaxVertex]; // 邻接矩阵 记录边信息 int vertexNum; // 邻接矩阵顶点数 int edgeNum; // 邻接矩阵边数 }; |
有向图与无向图融合版代码,只要更改flag值即可方便切换有向图与无向图邻接矩阵。
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #define FLAG 0 #define MaxVertex 50 // 最大顶点数 struct Graph { char vertex[MaxVertex][8]; // 顶点数组 假定为char类型 int edge[MaxVertex][MaxVertex]; // 邻接矩阵 记录边信息 int vertexNum; // 邻接矩阵顶点数 int edgeNum; // 邻接矩阵边数 }; int findVertex(struct Graph* graph, char* vertex) // 根据顶点值 查找顶点数组下标 { for (int i = 0; i < graph->vertexNum; i++) { if (strcmp(vertex, graph->vertex[i]) == 0) { return i; } } return INT_MAX; } void printfGraph(struct Graph* graph) // 遍历邻接矩阵 { printf("\t"); for (int i = 0; i < graph->vertexNum; i++) { printf("%s\t", graph->vertex[i]); } printf("\n"); for (int i = 0; i < graph->vertexNum; i++) { printf("%s\t", graph->vertex[i]); for (int j = 0; j < graph->vertexNum; j++) { if (graph->edge[i][j] == INT_MAX) { printf("∞\t"); } else { printf("%d\t", graph->edge[i][j]); } } printf("\n"); } } int main() { struct Graph* graph = malloc(sizeof(struct Graph)); #if FLAG printf("请输入无向图的顶点数和边数:\n顶点 边。\n"); #else printf("请输入有向图的顶点数和边数:\n顶点 边。\n"); #endif scanf("%d", &graph->vertexNum); scanf("%d", &graph->edgeNum); printf("请输入%d个顶点的值。\n", graph->vertexNum); for (int i = 0; i < graph->vertexNum; i++) { scanf("%s", graph->vertex[i]); } for (int i = 0; i < graph->vertexNum; i++) // 初始化邻接矩阵时间O(n^2) { for (int j = 0; j < graph->vertexNum; j++) { #if FLAG graph->edge[i][j] = 0; #else graph->edge[i][j] = INT_MAX; #endif } } #if FLAG printf("请输入%d条边:\n顶点1 顶点2\n", graph->edgeNum); #else printf("请输入%d条边:\n弧尾 弧头 权重\n", graph->edgeNum); int weight; #endif char tail[8] = { 0 }, head[8] = { 0 }; for (int i = 1; i <= graph->edgeNum; i++) { printf("请输入第%d条边:\n", i); scanf("%s", tail); scanf("%s", head); #if !FLAG scanf("%d", &weight); #endif int tailIndex = findVertex(graph, tail); int headIndex = findVertex(graph, head); if (tailIndex == INT_MAX || headIndex == INT_MAX) { #if FLAG printf("输入的顶点不存在,请重新输入:\n"); #else printf("输入的狐尾和狐头不存在,请重新输入:\n"); #endif i--; continue; } #if FLAG graph->edge[tailIndex][headIndex] = 1; graph->edge[headIndex][tailIndex] = 1; #else graph->edge[tailIndex][headIndex] = weight; #endif } printfGraph(graph); return 0; } |
由于无向网的邻接矩阵是对称的,可采用压缩存储仅存储主对角线以下的元素。建立一个无向网的算法。
特点:
1.无向图的邻接矩阵是对称的。
2.顶点i的度等于第i行(列)1的个数。
3.完全图的邻接矩阵中,对角元素为0,其余为1。
运行结果:
请输入图的顶点数和边数: 顶点 边。 4 5 请输入4个顶点的值。 v0 v1 v2 v3 请输入5条边: 顶点1 顶点2 请输入第1条边: v0 v1 请输入第2条边: v0 v2 请输入第3条边: v1 v2 请输入第4条边: v2 v3 请输入第5条边: v3 v0 v0 v1 v2 v3 v0 0 1 1 1 v1 1 0 1 0 v2 1 1 0 1 v3 1 0 1 0 |
上述算法的执行时间是O(n2+e+n),其中O(n2)是初始化邻接矩阵所耗费的时间。因此,该算法的时间复杂度应为O(n2)。
创建邻接矩阵:
|
邻接矩阵优点:
· 无向图:对应行(或列)非0元素个数 · 有向图:对应行非0元素的个数是“出度”;对应列非0元素的个数是“入度”。 |
邻接矩阵缺点: · 不便于增加和删除顶点 · 浪费空间——存稀疏图(点很多而边很少)有大量无效元素 · 对稠密图(特别是完全图)空间利用率才高 · 浪费时间——统计稀疏图中一共有多少条边,算法的时间复杂度应为O(n2)。 |
邻接矩阵特性: 1.无向图的邻接矩阵是对称的; 2.顶点i的度=第i行(列)中1的个数; 特别:完全图的邻接矩阵中,对角元素为0,其余为1; |
有向图邻接矩阵。
第i行含义:以结点Vi为尾的弧(出度边),第i列含义:以结点Vi为头的弧(入度边)。
特点:
1.有向图的邻接矩阵可能是不对称的。
2.顶点的出度等于第i行元素之和。
3.顶点的入度等于第i列元素之和。
4.顶点的度等于第i行元素之和加上第i列元素之和。
运行结果:
请输入图的顶点数和边数: 顶点 边。 5 6 请输入5个顶点的值。 v0 v1 v2 v3 v4 请输入6条边: 弧尾 弧头 权重 请输入第1条边: v0 v4 6 请输入第2条边: v1 v0 9 请输入第3条边: v1 v2 3 请输入第4条边: v2 v0 2 请输入第5条边: v2 v3 5 请输入第6条边: v3 v4 1 v0 v1 v2 v3 v4 v0 ∞ ∞ ∞ ∞ 6 v1 9 ∞ 3 ∞ ∞ v2 2 ∞ ∞ 5 ∞ v3 ∞ ∞ ∞ ∞ 1 v4 ∞ ∞ ∞ ∞ ∞ |
封装
头文件AMGraph.h:
#pragma once #ifndef ADJACENT_MATRIX_GRAPH_H #define ADJACENT_MATRIX_GRAPH_H #include "linkQueue.h" #include "sequenceQueue.h" #include "dynamicArray.h" #include "sparseMatrix.h" // Adjacent<əˈdʒeɪs(ə)nt> Matrix<ˈmeɪtrɪks> Undigraph<ənˈdaɪɡrɑːf> struct AMGraph { int isdigraph; struct AdjacentMatrixGraph* graph; void (*print)(struct AMGraph* aMGraph); void (*depthFirstSearch)(struct AMGraph* aMGraph); void (*breadthFirstSearch)(struct AMGraph* aMGraph); struct AMGraph* (*pinm)(struct AMGraph* aMGraph); struct AMGraph* (*kruskal)(struct AMGraph* aMGraph); void (*dijkstra)(struct AMGraph* aMGraph); void (*floyd)(struct AMGraph* aMGraph); int (*destroy)(struct AMGraph* aMGraph); }; // Please input NULL OR tell me the path, if write in hard disk. struct AMGraph* initAMGraph(int isdigraph, char* path); struct AMGraph* readAMGraphByDisk(int isdigraph, char* path); #endif |
逻辑实现AMGraph.c:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #define MODE 0 // 切换链式队列/顺序队列 #include "AMGraph.h" struct AdjacentMatrixGraph* createAMGraph(int isdigraph); void writeAMGraphInHardDisk(char* path, struct AMGraph* aMGraph); void printAMGraph(struct AMGraph* aMGraph); void depthFirstSearchAMGraph(struct AMGraph* aMGraph); void breadthFirstSearchAMGraphSequence(struct AMGraph* aMGraph); void breadthFirstSearchAMGraphLink(struct AMGraph* aMGraph); struct AMGraph* pinmAMGraph(struct AMGraph* aMGraph); struct AMGraph* kruskalAMGraph(struct AMGraph* aMGraph); void dijkstraAMGraph(struct AMGraph* aMGraph); void floydAMGraph(struct AMGraph* aMGraph); int destroyAMGraph(struct AMGraph* aMGraph); struct AdjacentMatrixGraph { char** vertex; int** edge; int vertexNum; int edgeNum; }; struct AMGraph* initAMGraphTail(struct AMGraph* aMGraph) { aMGraph->print = printAMGraph; aMGraph->depthFirstSearch = depthFirstSearchAMGraph; aMGraph->breadthFirstSearch = MODE ? breadthFirstSearchAMGraphLink : breadthFirstSearchAMGraphSequence; aMGraph->destroy = destroyAMGraph; aMGraph->pinm = pinmAMGraph; aMGraph->kruskal = kruskalAMGraph; aMGraph->dijkstra = dijkstraAMGraph; aMGraph->floyd = floydAMGraph; return aMGraph; } struct AMGraph* initAMGraph(int isdigraph, char* path) { struct AMGraph* aMGraph = malloc(sizeof(struct AMGraph)); struct AdjacentMatrixGraph* graph = createAMGraph(isdigraph); if (aMGraph == NULL || graph == NULL) { printf("创建邻接矩阵--%s图,分配内存空间失败!\n", isdigraph ? "有向" : "无向"); return NULL; } aMGraph->isdigraph = isdigraph; aMGraph->graph = graph; if (path != NULL) { writeAMGraphInHardDisk(path, aMGraph); } return initAMGraphTail(aMGraph); } struct AMGraph* readAMGraphByDisk(int isdigraph, char* path) { if (path == NULL) { printf("读取邻接矩阵--%s图,Input path can not be null.\n", isdigraph ? "有向" : "无向"); return NULL; } printf("邻接矩阵--%s图读出磁盘%s保存的数据!\n", isdigraph ? "有向" : "无向", path); FILE* file = fopen(path, "rb+"); if (file == NULL) { printf("邻接矩阵--%s图,Open file can not be null!\n", isdigraph ? "有向" : "无向"); return NULL; } struct AMGraph* aMGraph = malloc(sizeof(struct AMGraph)); struct AdjacentMatrixGraph* graph = malloc(sizeof(struct AdjacentMatrixGraph)); if (aMGraph == NULL || graph == NULL) { printf("读取邻接矩阵--%s图,分配内存空间失败!\n", isdigraph ? "有向" : "无向"); return NULL; } fread(graph, sizeof(struct AdjacentMatrixGraph), 1, file); aMGraph->isdigraph = isdigraph; aMGraph->graph = graph; char** vertex = malloc(sizeof(char*) * graph->vertexNum); int** edge = malloc(sizeof(int*) * graph->vertexNum); if (vertex == NULL || edge == NULL) { printf("读取邻接矩阵--%s图顶点数组,分配内存空间失败!\n", isdigraph ? "有向" : "无向"); return NULL; } graph->vertex = vertex; graph->edge = edge; for (int i = 0; i < graph->vertexNum; i++) { char* ch = malloc(sizeof(char) * 8); if (ch == NULL) { printf("读取邻接矩阵--%s图顶点,分配内存空间失败!\n", isdigraph ? "有向" : "无向"); return NULL; } fread(ch, sizeof(char) * 8, 1, file); vertex[i] = ch; } for (int i = 0; i < graph->vertexNum; i++) { int* rowEdge = malloc(sizeof(int) * graph->vertexNum); if (rowEdge == NULL) { printf("读取邻接矩阵--%s图边数据,分配内存空间失败!\n", isdigraph ? "有向" : "无向"); return NULL; } edge[i] = rowEdge; for (int j = 0; j < graph->vertexNum; j++) { fread(&edge[i][j], sizeof(int), 1, file); } } fclose(file); return initAMGraphTail(aMGraph); } int findVertexOfAMGraph(struct AdjacentMatrixGraph* graph, char* vertex, int isdigraph) { if (graph == NULL) { printf("邻接矩阵--%s图不存在!\n", isdigraph ? "有向" : "无向"); return -1; } for (int i = 0; i < graph->vertexNum; i++) { if (strcmp(vertex, graph->vertex[i]) == 0) { return i; } } return INT_MAX; } struct AdjacentMatrixGraph* createAMGraph(int isdigraph) { struct AdjacentMatrixGraph* graph = malloc(sizeof(struct AdjacentMatrixGraph)); printf("请输入%s图的顶点数和边数:\n顶点 边。\n", isdigraph ? "有向" : "无向"); scanf("%d", &graph->vertexNum); scanf("%d", &graph->edgeNum); char** vertex = malloc(sizeof(char*) * graph->vertexNum); int** edge = malloc(sizeof(int*) * graph->vertexNum); if (vertex == NULL || edge == NULL) { printf("开辟%s图顶点空间失败,内存空间不足!\n", isdigraph ? "有向" : "无向"); return NULL; } graph->vertex = vertex; graph->edge = edge; printf("请输入%d个顶点的值。\n", graph->vertexNum); for (int i = 0; i < graph->vertexNum; i++) { char* ch = malloc(sizeof(char) * 8); scanf("%s", ch); graph->vertex[i] = ch; } for (int i = 0; i < graph->vertexNum; i++) { int* rowEdge = malloc(sizeof(int) * graph->vertexNum); if (rowEdge == NULL) { printf("开辟%s图边空间失败,内存空间不足!\n", isdigraph ? "有向" : "无向"); return NULL; } graph->edge[i] = rowEdge; for (int j = 0; j < graph->vertexNum; j++) { if (isdigraph) { graph->edge[i][j] = INT_MAX; } else { graph->edge[i][j] = 0; } } } printf("请输入%d条边:\n%s\n", graph->edgeNum, isdigraph ? "弧尾 弧头 权重" : "顶点1 顶点2 权重(无权值为1)"); int weight = INT_MAX; char tail[8] = { 0 }, head[8] = { 0 }; for (int i = 1; i <= graph->edgeNum; i++) { printf("请输入第%d条边:\n", i); scanf("%s", tail); scanf("%s", head); scanf("%d", &weight); int tailIndex = findVertexOfAMGraph(graph, tail, isdigraph); int headIndex = findVertexOfAMGraph(graph, head, isdigraph); if (tailIndex == INT_MAX || headIndex == INT_MAX) { printf("输入的顶点不存在,请重新输入:\n"); i--; continue; } if (tailIndex == headIndex) { printf("输入的顶点不能指向自身,请重新输入:\n"); i--; continue; } if (edge[tailIndex][headIndex] != INT_MAX && edge[tailIndex][headIndex] != 0 && weight != INT_MAX) { printf("输入的边或弧已存在,更新权值,请重新输入新边或弧:\n"); graph->edge[tailIndex][headIndex] = weight; i--; continue; } if (isdigraph) { graph->edge[tailIndex][headIndex] = weight; } else { graph->edge[tailIndex][headIndex] = weight; graph->edge[headIndex][tailIndex] = weight; } } return graph; } void writeAMGraphInHardDisk(char* path, struct AMGraph* aMGraph) { if (aMGraph == NULL || aMGraph->graph == NULL) { printf("邻接矩阵--图不存在!\n"); return; } struct AdjacentMatrixGraph* graph = aMGraph->graph; printf("邻接矩阵--%s图写入磁盘%s!\n", aMGraph->isdigraph ? "有向" : "无向", path); FILE* file = fopen(path, "wb+"); if (file == NULL) { printf("邻接矩阵--%s图,Open file can not be null!\n", aMGraph->isdigraph ? "有向" : "无向"); return; } fwrite(graph, sizeof(struct AdjacentMatrixGraph), 1, file); char** vertex = graph->vertex; for (int i = 0; i < graph->vertexNum; i++) { fwrite(vertex[i], sizeof(char) * 8, 1, file); } int** edge = graph->edge; for (int i = 0; i < graph->vertexNum; i++) { for (int j = 0; j < graph->vertexNum; j++) { fwrite(&edge[i][j], sizeof(int), 1, file); } } fclose(file); } void printAMGraph(struct AMGraph* aMGraph) { if (aMGraph == NULL || aMGraph->graph == NULL) { printf("邻接矩阵--图不存在!\n"); return; } struct AdjacentMatrixGraph* graph = aMGraph->graph; printf("邻接矩阵--%s图结构:\n\t", aMGraph->isdigraph ? "有向" : "无向"); for (int i = 0; i < graph->vertexNum; i++) { printf("%s\t", graph->vertex[i]); } printf("\n"); for (int i = 0; i < graph->vertexNum; i++) { printf("%s\t", graph->vertex[i]); for (int j = 0; j < graph->vertexNum; j++) { if (graph->edge[i][j] == INT_MAX) { printf("∞\t"); } else { printf("%d\t", graph->edge[i][j]); } } printf("\n"); } } // -1:成功; -2:回参表示回路/入参表示空值; -3:异常; >=0:顶点索引 int recursionAMGraph(int* visited, int index, struct AdjacentMatrixGraph* graph, int (*execute)(struct AdjacentMatrixGraph* graph, int index), int adjvex) { if (adjvex == -2) { if (!execute(graph, index)) return index; } else if (adjvex == index) { return -2; } // printf("%s ", graph->vertex[index]); visited[index] = 1; for (int i = 0; i < (int)graph->vertexNum; i++) { if (!visited[i] && graph->edge[index][i] > 0 && graph->edge[index][i] != INT_MAX) { int result = recursionAMGraph(visited, i, graph, execute, adjvex); if (result != -1) { return result; } } } return -1; } void depthFirstSearchAMGraph(struct AMGraph* aMGraph) { if (aMGraph == NULL || aMGraph->graph == NULL) { printf("邻接矩阵--图不存在!\n"); return; } struct AdjacentMatrixGraph* graph = aMGraph->graph; printf("深度优先遍历邻接矩阵--%s图!\n", aMGraph->isdigraph ? "有向" : "无向"); int* visited = malloc(sizeof(int) * (unsigned int)graph->vertexNum); if (visited == NULL) return; memset(visited, 0, sizeof(int) * (int)graph->vertexNum); int depthFirstSearchPrint(struct AdjacentMatrixGraph* graph, int index); for (int i = 0; i < graph->vertexNum; i++) { if (!visited[i]) { recursionAMGraph(visited, i, graph, depthFirstSearchPrint, -2); } } free(visited); visited = NULL; } int depthFirstSearchPrint(struct AdjacentMatrixGraph* graph, int index) { printf("%s ", graph->vertex[index]); return 1; } // -1:成功; -2:回参表示回路/入参表示空值; -3:异常; >=0:顶点索引 int breadthSearchAMGraphSequence(struct SequenceQueue* queue, int* visited, int index, struct AdjacentMatrixGraph* graph, int (*execute)(struct AdjacentMatrixGraph* graph, int index), int adjvex) { visited[index] = 1; int* num = malloc(sizeof(int)); if (num == NULL) return -3; *num = index; queue->push(num, queue); if (!execute(graph, index)) return index; while (queue->isNotEmpty(queue)) { int* front = queue->front(queue); for (int i = 0; i < (int)graph->vertexNum; i++) { if (!visited[i] && graph->edge[*front][i] > 0) { if (adjvex == -2) { if (!execute(graph, i)) return i; } else if (adjvex == i) { return -2; } visited[i] = 1; int* index = malloc(sizeof(int)); if (index == NULL) return -3; *index = i; queue->push(index, queue); } } int* index = queue->pop(queue); free(index); index = NULL; } return -1; } void breadthFirstSearchAMGraphSequence(struct AMGraph* aMGraph) { if (aMGraph == NULL || aMGraph->graph == NULL) { printf("邻接矩阵--图不存在!\n"); return; } struct AdjacentMatrixGraph* graph = aMGraph->graph; printf("广度优先遍历邻接矩阵--%s图:\n", aMGraph->isdigraph ? "有向" : "无向"); int* visited = malloc(sizeof(int) * (int)graph->vertexNum); if (visited == NULL) return; memset(visited, 0, sizeof(int) * (int)graph->vertexNum); struct SequenceQueue* queue = initSequenceQueue(); int breadthSearchAMGraphPrint(struct AdjacentMatrixGraph* graph, int index); for (int i = 0; i < graph->vertexNum; i++) { if (!visited[i]) { breadthSearchAMGraphSequence(queue, visited, i, graph, breadthSearchAMGraphPrint, -2); } } free(visited); visited = NULL; free(queue); queue = NULL; } int breadthSearchAMGraphPrint(struct AdjacentMatrixGraph* graph, int index) { printf("%s ", graph->vertex[index]); return 1; } struct Num { void* next; int index; }; // -1:成功; -2:回参表示回路/入参表示空值; -3:异常; >=0:顶点索引 int breadthSearchAMGraphLink(struct LinkQueue* queue, int* visited, int index, struct AdjacentMatrixGraph* graph, int (*execute)(struct AdjacentMatrixGraph* graph, int index), int adjvex) { visited[index] = 1; struct Num* num = malloc(sizeof(struct Num)); if (num == NULL) return -3; num->index = index; queue->push(num, queue); if (!execute(graph, index)) return index; while (queue->isNotEmpty(queue)) { struct Num* front = queue->front(queue); for (int i = 0; i < (int)graph->vertexNum; i++) { if (!visited[i] && graph->edge[front->index][i] > 0) { if (adjvex == -2) { if (!execute(graph, i)) return i; } else if (adjvex == i) { return -2; } visited[i] = 1; struct Num* index = malloc(sizeof(struct Num)); if (index == NULL) return -3; index->index = i; queue->push(index, queue); } } int* index = queue->pop(queue); free(index); index = NULL; } return -1; } void breadthFirstSearchAMGraphLink(struct AMGraph* aMGraph) { if (aMGraph == NULL || aMGraph->graph == NULL) { printf("邻接矩阵--图不存在!\n"); return; } struct AdjacentMatrixGraph* graph = aMGraph->graph; printf("广度优先遍历邻接矩阵--%s图:\n", aMGraph->isdigraph ? "有向" : "无向"); int* visited = malloc(sizeof(int) * (int)graph->vertexNum); if (visited == NULL) return; memset(visited, 0, sizeof(int) * (int)graph->vertexNum); struct LinkQueue* queue = initLinkQueue(); for (int i = 0; i < graph->vertexNum; i++) { if (!visited[i]) { breadthSearchAMGraphLink(queue, visited, i, graph, breadthSearchAMGraphPrint, -2); } } free(visited); visited = NULL; free(queue); queue = NULL; } struct AMGraph* pinmAMGraph(struct AMGraph* aMGraph) { if (aMGraph == NULL || aMGraph->graph == NULL) { printf("邻接矩阵--图,不存在!\n"); return NULL; } struct AdjacentMatrixGraph* graph = aMGraph->graph; struct AdjacentMatrixGraph* initEmptyAMGraph(int isdigraph, int size); struct AdjacentMatrixGraph* minTree = initEmptyAMGraph(aMGraph->isdigraph, aMGraph->graph->vertexNum); minTree->vertex = graph->vertex; minTree->vertexNum = graph->vertexNum; minTree->edgeNum = graph->edgeNum; void pinmCreateMinTree(struct AdjacentMatrixGraph* graph, struct AdjacentMatrixGraph* minTree, int vertexIndex); pinmCreateMinTree(graph, minTree, 0); // void pinmMinTree(struct AMGraph* aMGraph, struct AdjacentMatrixGraph* minTree); // pinmMinTree(aMGraph, minTree); struct AMGraph* minGraph = malloc(sizeof(struct AMGraph)); if (minGraph == NULL) { printf("邻接矩阵--%s图生成最小生成树内存不足!\n", aMGraph->isdigraph ? "有向" : "无向"); return NULL; } minGraph->isdigraph = aMGraph->isdigraph; minGraph->graph = minTree; return initAMGraphTail(minGraph); } struct AdjacentMatrixGraph* initEmptyAMGraph(int isdigraph, int size) { struct AdjacentMatrixGraph* graph = malloc(sizeof(struct AdjacentMatrixGraph)); if (graph == NULL) { printf("开辟%s图空间失败,内存空间不足!\n", isdigraph ? "有向" : "无向"); return NULL; } graph->vertex = NULL; graph->vertexNum = 0; graph->edge = NULL; graph->edgeNum = 0; int** edge = malloc(sizeof(int*) * size); if (edge == NULL) { printf("开辟%s图顶点空间失败,内存空间不足!\n", isdigraph ? "有向" : "无向"); return NULL; } graph->edge = edge; for (int i = 0; i < size; i++) { int* rowEdge = malloc(sizeof(int) * size); if (rowEdge == NULL) { printf("开辟%s图边空间失败,内存空间不足!\n", isdigraph ? "有向" : "无向"); return NULL; } graph->edge[i] = rowEdge; for (int j = 0; j < size; j++) { if (isdigraph) { graph->edge[i][j] = INT_MAX; } else { graph->edge[i][j] = 0; } } } return graph; } struct MinWeight { int adjvex; int weight; }; void pinmCreateMinTree(struct AdjacentMatrixGraph* graph, struct AdjacentMatrixGraph* minTree, int vertexIndex) { int size = graph->vertexNum; struct MinWeight* minWeightArray = malloc(sizeof(struct MinWeight) * size); for (int i = 0; i < size; i++) { if (i != vertexIndex) { minWeightArray[i].adjvex = vertexIndex; if (graph->edge[i][vertexIndex] == 0) { minWeightArray[i].weight = INT_MAX; } else { minWeightArray[i].weight = graph->edge[i][vertexIndex]; } } } minWeightArray[vertexIndex].weight = 0; int findMinWeight(struct MinWeight* minWeightArray, int size); for (int i = 0; i < size - 1; i++) { vertexIndex = findMinWeight(minWeightArray, size); minTree->edge[vertexIndex][minWeightArray[vertexIndex].adjvex] = minWeightArray[vertexIndex].weight; minTree->edge[minWeightArray[vertexIndex].adjvex][vertexIndex] = minWeightArray[vertexIndex].weight; minWeightArray[vertexIndex].weight = 0; for (int j = 0; j < size; j++) { if (graph->edge[vertexIndex][j] < minWeightArray[j].weight && graph->edge[vertexIndex][j] > 0) { minWeightArray[j].adjvex = vertexIndex; minWeightArray[j].weight = graph->edge[j][vertexIndex]; } } } free(minWeightArray); minWeightArray = NULL; } int findMinWeight(struct MinWeight* minWeightArray, int size) { int minWeight = INT_MAX, index = -1; for (int i = 0; i < size; i++) { if (minWeightArray[i].weight < minWeight && minWeightArray[i].weight > 0) { minWeight = minWeightArray[i].weight; index = i; } } return index; } //==================================================================↓↓↓=保留pinm算法=↓↓↓================================================================= void pinmMinTree(struct AMGraph* aMGraph, struct AdjacentMatrixGraph* minTree) { struct AdjacentMatrixGraph* graph = aMGraph->graph; int size = graph->vertexNum; struct MinWeight* cacheArray = malloc(sizeof(struct MinWeight) * size); if (cacheArray == NULL) { printf("邻接矩阵--%s图记录最小生成树内存不足!\n", aMGraph->isdigraph ? "有向" : "无向"); return; } for (int i = 0; i < size; i++) { cacheArray[i].adjvex = -1; cacheArray[i].weight = -1; } struct ArrayList* vertexArray = initArrayList(size); int in = 0; vertexArray->insert(0, &in, vertexArray); int** edge = graph->edge; for (int i = 1; i < size; i++) { int lenght = vertexArray->size(vertexArray); for (int j = 0; j < lenght; j++) { int minWeight = INT_MAX, * vertex = vertexArray->get(j, vertexArray), adjvex = -1; for (int k = 0; k < size; k++) { int compareVertex(void* firstData, void* secondData); if (edge[*vertex][k] > 0 && edge[*vertex][k] < minWeight && vertexArray->getData(&k, compareVertex, vertexArray) == NULL) { minWeight = edge[*vertex][k]; adjvex = k; } } cacheArray[*vertex].adjvex = adjvex; cacheArray[*vertex].weight = minWeight; } int* adjvex = malloc(sizeof(int)); if (adjvex == NULL) { printf("邻接矩阵--%s图记录最小生成树内存不足!\n", aMGraph->isdigraph ? "有向" : "无向"); return; } *adjvex = -1; int minWeight = INT_MAX, vertex = -1; for (int j = 0; j < size; j++) { if (cacheArray[j].weight < minWeight && cacheArray[j].weight != -1) { vertex = j; *adjvex = cacheArray[j].adjvex; minWeight = cacheArray[j].weight; } cacheArray[j].adjvex = -1; cacheArray[j].weight = -1; } minTree->edge[*adjvex][vertex] = minWeight; minTree->edge[vertex][*adjvex] = minWeight; vertexArray->insert(0, adjvex, vertexArray); } vertexArray->ruin(vertexArray); free(cacheArray); cacheArray = NULL; } int compareVertex(void* firstData, void* secondData) { int* first = firstData; int* second = secondData; if (*first == *second) { return 1; } return 0; } //==================================================================↑↑↑=保留pinm算法=↑↑↑================================================================= struct AMGraph* kruskalAMGraph(struct AMGraph* aMGraph) { if (aMGraph == NULL || aMGraph->graph == NULL) { printf("邻接矩阵--图,不存在!\n"); return NULL; } struct AdjacentMatrixGraph* graph = aMGraph->graph; struct AdjacentMatrixGraph* initEmptyAMGraph(int isdigraph, int size); struct AdjacentMatrixGraph* minTree = initEmptyAMGraph(aMGraph->isdigraph, aMGraph->graph->vertexNum); minTree->vertex = graph->vertex; minTree->vertexNum = graph->vertexNum; minTree->edgeNum = graph->edgeNum; void kruskalMinTree(struct AMGraph* aMGraph, struct AdjacentMatrixGraph* minTree); kruskalMinTree(aMGraph, minTree); struct AMGraph* minGraph = malloc(sizeof(struct AMGraph)); if (minGraph == NULL) { printf("邻接矩阵--%s图生成最小生成树内存不足!\n", aMGraph->isdigraph ? "有向" : "无向"); return NULL; } minGraph->isdigraph = aMGraph->isdigraph; minGraph->graph = minTree; return initAMGraphTail(minGraph); } struct ASCWeight { int vertex; int adjvex; int weight; }; void kruskalMinTree(struct AMGraph* aMGraph, struct AdjacentMatrixGraph* minTree) { struct AdjacentMatrixGraph* graph = aMGraph->graph; int size = graph->edgeNum; struct ASCWeight* getSortASCWeight(struct AdjacentMatrixGraph* graph, int isdigraph); struct ASCWeight* weightArray = getSortASCWeight(graph, aMGraph->isdigraph); minTree->edge[weightArray[0].vertex][weightArray[0].adjvex] = weightArray[0].weight; int isLoop(struct AdjacentMatrixGraph* minTree, int vertex, int adjvex); for (int i = 1; i < size; i++) { if (!isLoop(minTree, weightArray[i].vertex, weightArray[i].adjvex)) { minTree->edge[weightArray[i].vertex][weightArray[i].adjvex] = weightArray[i].weight; minTree->edge[weightArray[i].adjvex][weightArray[i].vertex] = weightArray[i].weight; } } free(weightArray); weightArray = NULL; } struct ASCWeight* getSortASCWeight(struct AdjacentMatrixGraph* graph, int isdigraph) { int edgeSize = graph->edgeNum, size = graph->vertexNum; struct ASCWeight* weightArray = malloc(sizeof(struct ASCWeight) * edgeSize); if (weightArray == NULL) { printf("邻接矩阵--%s图生成最小生成树构建辅助数组内存不足!\n", isdigraph ? "有向" : "无向"); return NULL; } int** edge = graph->edge, index = 0; for (int i = 0; i < size; i++) { for (int j = i; j < size; j++) { if (edge[i][j] > 0 && edge[i][j] < INT_MAX) { weightArray[index].vertex = i; weightArray[index].adjvex = j; weightArray[index].weight = edge[i][j]; index++; } } } void sortWeightArray(struct ASCWeight* weightArray, int size); sortWeightArray(weightArray, index); return weightArray; } void sortWeightArray(struct ASCWeight* weightArray, int size) { int flag = 0; struct ASCWeight* temp = malloc(sizeof(struct ASCWeight)); if (temp == NULL) { printf("生成最小生成数,排序临时分配空间失败,内存不足!"); return; } for (int i = 0; i < size; i++) { for (int j = 0; j < size - 2 - i; j++) { if (weightArray[j].weight > weightArray[j + 1].weight) { flag = 0; temp->vertex = weightArray[j].vertex; temp->adjvex = weightArray[j].adjvex; temp->weight = weightArray[j].weight; weightArray[j].vertex = weightArray[j + 1].vertex; weightArray[j].adjvex = weightArray[j + 1].adjvex; weightArray[j].weight = weightArray[j + 1].weight; weightArray[j + 1].vertex = temp->vertex; weightArray[j + 1].adjvex = temp->adjvex; weightArray[j + 1].weight = temp->weight; } else { flag++; } } if (flag == (size - 2 - i)) { break; } flag = 0; } free(temp); temp = NULL; } int isLoop(struct AdjacentMatrixGraph* minTree, int vertex, int adjvex) { int size = minTree->vertexNum; int* visited = malloc(sizeof(int) * size); if (visited == NULL) return 1; memset(visited, 0, sizeof(int) * size); int verifyLoop(struct AdjacentMatrixGraph* graph, int index); int isAdjvex = recursionAMGraph(visited, vertex, minTree, verifyLoop, adjvex); if (isAdjvex == -2) { return 1; } return 0; } int verifyLoop(struct AdjacentMatrixGraph* graph, int index) { return 1; } struct Dijkstra { int* vertex; int isMinPath; int priorVertex; int weight; }; void dijkstraAMGraph(struct AMGraph* aMGraph) { if (aMGraph == NULL || aMGraph->graph == NULL) { printf("邻接矩阵--图,不存在!\n"); return; } struct AdjacentMatrixGraph* graph = aMGraph->graph; int size = graph->vertexNum; struct Dijkstra* dijkstra = malloc(sizeof(struct Dijkstra) * size); int* vertex = malloc(sizeof(int) * size); if (dijkstra == NULL || vertex == NULL) { printf("构建Dijkstra数组失败,内车内空间不足!\n"); return; } int index = 0, vertexIndex = 0; for (int i = 0; i < size; i++) { dijkstra[i].vertex = vertex; dijkstra[i].isMinPath = 0; dijkstra[i].weight = graph->edge[index][i]; if (dijkstra[i].weight < INT_MAX && dijkstra[i].weight > 0) { dijkstra[i].priorVertex = index; } else { dijkstra[i].priorVertex = -1; } } dijkstra[index].vertex[vertexIndex++] = index; dijkstra[index].isMinPath = 1; dijkstra[index].weight = 0; for (int i = 1; i < size; i++) { int minWeight = INT_MAX, minVertex = -1; for (int j = 0; j < size; j++) { if (!dijkstra[j].isMinPath && dijkstra[j].weight < minWeight) { minWeight = dijkstra[j].weight; minVertex = j; } } dijkstra[index].vertex[vertexIndex++] = minVertex; dijkstra[minVertex].isMinPath = 1; for (int j = 0; j < size; j++) { if (!dijkstra[j].isMinPath && dijkstra[minVertex].weight + graph->edge[minVertex][j] > 0 && dijkstra[minVertex].weight + graph->edge[minVertex][j] < dijkstra[j].weight) { dijkstra[j].priorVertex = minVertex; dijkstra[j].weight = dijkstra[minVertex].weight + graph->edge[minVertex][j]; } } } for (int i = 0; i < size; i++) { if (dijkstra[i].priorVertex == -1) continue; vertexIndex = 0; printf("顶点"); while (1) { int searchIndex = dijkstra[i].vertex[vertexIndex]; printf("%s+", graph->vertex[searchIndex]); if (searchIndex == dijkstra[i].priorVertex) { printf("%s=权值:%d\n", graph->vertex[i], dijkstra[i].weight); break; } vertexIndex++; } } } // int元素类型数组拷贝功能 暂时放在这里 将来找到新位置了 过来迁移 copyIntArray(int* source, int index, int* taget, int start, int lenght) { for (int i = index, j = start; i < index + lenght; i++, j++) { taget[j] = source[i]; } } struct Floyd { int capacity; int* vertex; int size; int weight; }; void floydAMGraph(struct AMGraph* aMGraph) { if (aMGraph == NULL || aMGraph->graph == NULL) { printf("邻接矩阵--图,不存在!\n"); return; } struct AdjacentMatrixGraph* graph = aMGraph->graph; int size = graph->vertexNum; struct Floyd** initFloydArray(struct AMGraph* aMGraph); struct Floyd** floydArray = initFloydArray(aMGraph); int** edge = graph->edge; for (int v = 0; v < size; v++) { for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { if (i == j) continue; if (floydArray[i][v].weight + floydArray[v][j].weight < floydArray[i][j].weight && floydArray[i][v].weight + floydArray[v][j].weight > 0) { memset(floydArray[i][j].vertex, -1, sizeof(int) * floydArray[i][j].capacity); copyIntArray(floydArray[i][v].vertex, 0, floydArray[i][j].vertex, 0, floydArray[i][v].size); floydArray[i][j].size = floydArray[i][v].size; copyIntArray(floydArray[v][j].vertex, 0, floydArray[i][j].vertex, floydArray[i][j].size, floydArray[v][j].size); floydArray[i][j].size = floydArray[i][j].size + floydArray[v][j].size; floydArray[i][j].weight = floydArray[i][v].weight + floydArray[v][j].weight; } } } } for (int i = 0; i < size; i++) { printf("顶点%s到其它顶点的最短距离:\n", graph->vertex[i]); for (int j = 0; j < size; j++) { if (i == j) continue; int lenght = floydArray[i][j].size, vertexIndex = 0;; while (vertexIndex < lenght) { int searchIndex = floydArray[i][j].vertex[vertexIndex]; printf("%s+", graph->vertex[searchIndex]); vertexIndex++; if (vertexIndex == lenght) { if (floydArray[i][j].weight == INT_MAX) { printf("%s=权值:∞\n", graph->vertex[j]); } else { printf("%s=权值:%d\n", graph->vertex[j], floydArray[i][j].weight); } } } } printf("\n"); } return; } struct Floyd** initFloydArray(struct AMGraph* aMGraph) { struct AdjacentMatrixGraph* graph = aMGraph->graph; int size = graph->vertexNum; struct Floyd** floydArray = malloc(sizeof(struct Floyd*) * size); if (floydArray == NULL) { printf("Floyd算法开辟矩阵内存空间不足!\n"); return; } for (int i = 0; i < size; i++) { struct Floyd* floyd = malloc(sizeof(struct Floyd) * size); if (floyd == NULL) { printf("Floyd算法开辟矩阵行内存空间不足!\n"); return; } floydArray[i] = floyd; for (int j = 0; j < size; j++) { int* vertex = malloc(sizeof(int) * size); if (vertex == NULL) { printf("Floyd算法开辟矩阵元素内存空间不足!\n"); return; } memset(vertex, -1, size); floyd[j].vertex = vertex; floyd[j].capacity = size; floyd[j].size = 0; floyd[j].weight = graph->edge[i][j]; if (i == j) { floyd[j].weight = 0; } else { vertex[floyd[j].size++] = i; } } } return floydArray; } int destroyAMGraph(struct AMGraph* aMGraph) { if (aMGraph == NULL || aMGraph->graph == NULL) { printf("邻接矩阵--图,不存在!\n"); return 0; } struct AdjacentMatrixGraph* graph = aMGraph->graph; int size = graph->vertexNum; char** vertex = graph->vertex; int** edge = graph->edge; for (int i = 0; i < size; i++) { free(vertex[i]); for (int j = 0; j < size; i++) { free(&edge[i][j]); } free(edge[i]); } free(vertex); vertex = NULL; free(edge); edge = NULL; free(graph); graph = NULL; free(aMGraph); aMGraph = NULL; return 1; } |
测试:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "AMGraph.h" int main() { //struct AMGraph* graph = initAMGraph(1, NULL); //struct AMGraph* graph = initAMGraph(1, "D:/Graph.temp"); struct AMGraph* graph = readAMGraphByDisk(1, "D:/Graph.temp"); graph->print(graph); return 0; } |
运行结果:
邻接矩阵--有向图读出磁盘D:/Graph.temp保存的数据! 邻接矩阵--有向图结构: v0 v1 v2 v3 v0 ∞ 10 20 30 v1 ∞ ∞ 12 ∞ v2 ∞ ∞ ∞ 23 v3 ∞ ∞ ∞ ∞ |
6.2.2.邻接表
邻接表是图的一种链式存储结构。这种存储表示法类似于树的孩子链表表示法。对于图G中每个顶点vi把所有邻接于vi的顶点vy链成一个单链表,这个单链表称为顶点vi的邻接表。
邻接表中每一个表结点有两个域:其一为邻接点域adjvex,用以存放与顶点vi相邻接的顶点vy的序号j;其二为指针域next,用来将邻接表的所有结点链在一起。如果要表示边上的权值,那么就要再增设一个数据域。另外,为每个顶点vi的邻接表设置一个具有两个域的表头结点:一个域是顶点信息域data,另一个则是指针域(指向邻接表) firstEdge,它是vi的邻接表的头指针。为了便于随机访问任一顶点的邻接表,需要把这n个表头指针用一个一维数组存储起来,这个数组(向量)就构成了图的邻接表的表示,其中第i个分量存储vi邻接表的表头指针。这样,图G就可以由这个表头数组来表示和存取。
在数学中,向量(也称为欧几里得向量、几何向量、矢量),指具有大小(magnitude)和方向的量。它可以形象化地表示为带箭头的线段。箭头所指:代表向量的方向;线段长度:代表向量的大小。与向量对应的量叫做数量(物理学中称标量),数量(或标量)只有大小,没有方向。
由此,图的邻接表存储结构定义如下:
#define MaxVertex 50 struct EdgeNode { int adjvex; struct EdgeNode* next; int weight; }; struct EdgeHeader { char data[sizeof(int)]; struct EdgeNode* firstEdge; }; struct Graph { struct EdgeHeader* vertex[MaxVertex]; int vertexNum; int edgeNum; }; |
对于无向图而言, vi的邻接表中每个表结点都对应于与vi相关联的一条边;对于有向图来说, vi的邻接表中每个表结点都对应于以vi为起点射出的一条边。因此,我们将无向图邻接表称为边表,将有向图的邻接表称为出边表,将邻接表的表头向量称为顶点表。例如,对于图6.6中的无向图G,其邻接表表示如图6.9所示。
邻接表表示法特性: 若无向图中有n个顶点和e条边,则它的邻接表共有n个头结点和2e个表结点。 在无向图的邻接表中,顶点vi的度恰好是第i个链表中的结点数。 而在有向图中,第i个链表的结点数只是顶点vi的出度,为了求入度,必须遍历整个邻接表。在所有链表中,其邻接点域值为i的结点的个数是顶点vi的入度。 |
有向图除了有一个邻接表(以vi为起点),有时还需要建立一个逆邻接表(入边表),即以vi为端点的邻接表。例如,图G7的邻接表和逆邻接表如图6.10所示。
图的邻接表表示不是唯一的,这是因为在每个顶点的邻接表中,各边结点的链接次序可以是任意的,其具体链接次序与边的输入次序和生成算法有关。
在邻接表(或逆邻接表)表示中,每个边表对应于邻接矩阵的一行(或一列),边表中结点的个数等于邻接矩阵的一行(或一列)中非零元素的个数。
在邻接矩阵表示中,很容易判定(vi,vy)或<vi,vy>是否为图的一条边,只要判定矩阵中的第i行第j列上的元素是否为零即可。但是在邻接表表示中,需要扫描第i个边表,最坏情况下要耗费O(n)时间。
在邻接表上很容易找到任一顶点的第一个邻接点和下一个邻接点,但要判断任意两个顶点(vi和vy)之间是否有边相连,则需要搜索第i个或第j个链表,因此不如邻接矩阵表示法方便。
一个无向图的邻接表的结构。
从图中可以看出,顶点表的各个结点由data和firstedge两个域表示,data是数据域,存储顶点的信息,firstedge是指针域,指向边表的第一个结点,即此顶点的第一个邻接点。边表结点由adjvex和next两个域组成。adjvex是邻接点域,存储某顶点的邻接点在顶点表中的下标,next则存储指向边表中下一个结点的指针。
对于带权值的网图,可以在边表结点定义中再增加一个weight的数据域,存储权值信息即可,如下图所示。
下面给出一个无向图与有向图邻接表的建表算法(更改FLAG值即可实现有向图与无向图代码切换):
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #define FLAG 0 #define MaxVertex 50 struct EdgeNode { int adjvex; struct EdgeNode* next; int weight; }; struct EdgeHeader { char data[sizeof(int)]; struct EdgeNode* firstEdge; }; struct Graph { struct EdgeHeader* vertex[MaxVertex]; int vertexNum; int edgeNum; }; int findVertex(struct Graph* graph, char* vertex) { for (int i = 0; i < graph->vertexNum; i++) { if (strcmp(vertex, graph->vertex[i]->data) == 0) { return i; } } return INT_MAX; } void printfGraph(struct Graph* graph) { #if FLAG printf("无向图邻接表:\n"); #else printf("有向图邻接表:\n"); #endif for (int i = 0; i < graph->vertexNum; i++) { printf("%d %s", i, graph->vertex[i]->data); if (graph->vertex[i]->firstEdge == NULL) { printf("\n"); continue; } struct EdgeNode* currentNode = graph->vertex[i]; do { currentNode = currentNode->next; printf("---->%d", currentNode->adjvex); } while (currentNode->next != NULL); printf("\n"); } } void insertEdge(struct Graph* graph, int tailIndex, int headIndex) { struct EdgeNode* edgeNode = malloc(sizeof(struct EdgeNode)); if (edgeNode == NULL) { return; } edgeNode->adjvex = headIndex; edgeNode->next = NULL; if (graph->vertex[tailIndex]->firstEdge == NULL) { graph->vertex[tailIndex]->firstEdge = edgeNode; return; } struct EdgeNode* currentNode = graph->vertex[tailIndex]->firstEdge; struct EdgeNode* priorNode = graph->vertex[tailIndex]; // move the pointer while (currentNode->next != NULL && currentNode->adjvex < headIndex) { priorNode = currentNode; currentNode = currentNode->next; } // insert top if (headIndex < currentNode->adjvex) { priorNode->next = edgeNode; edgeNode->next = currentNode; } else if (headIndex > currentNode->adjvex) { // insert tail if (currentNode->next == NULL) { currentNode->next = edgeNode; } else { struct EdgeNode* nextNode = currentNode->next; currentNode->next = edgeNode; edgeNode->next = nextNode; } } else { return; } } int main() { struct Graph* graph = malloc(sizeof(struct Graph)); if (graph == NULL) { return; } printf("请输入图的顶点数和边数:\n顶点 边。\n"); scanf("%d", &graph->vertexNum); scanf("%d", &graph->edgeNum); printf("请输入%d个顶点的值。\n", graph->vertexNum); for (int i = 0; i < graph->vertexNum; i++) { struct EdgeHeader* vertex = malloc(sizeof(struct EdgeHeader)); if (vertex == NULL) { return; } graph->vertex[i] = vertex; scanf("%s", graph->vertex[i]->data); graph->vertex[i]->firstEdge = NULL; } printf("请输入%d条边:\n顶点1 顶点2\n", graph->edgeNum); char tail[8] = { 0 }, head[8] = { 0 }; for (int i = 1; i <= graph->edgeNum; i++) { printf("请输入第%d条边:\n", i); scanf("%s", tail); scanf("%s", head); int tailIndex = findVertex(graph, tail); int headIndex = findVertex(graph, head); if (tailIndex == INT_MAX || headIndex == INT_MAX) { printf("输入的顶点不存在,请重新输入:\n"); i--; continue; } insertEdge(graph, tailIndex, headIndex); #if FLAG insertEdge(graph, headIndex, tailIndex); #endif } printfGraph(graph); return 0; } |
运行结果(无向图):
请输入图的顶点数和边数: 顶点 边。 4 5 请输入4个顶点的值。 v0 v1 v2 v3 请输入5条边: 顶点1 顶点2 请输入第1条边: v0 v3 请输入第2条边: v0 v2 请输入第3条边: v0 v1 请输入第4条边: v3 v2 请输入第5条边: v2 v1 无向图邻接表: 0 v0---->1---->2---->3 1 v1---->0---->2 2 v2---->0---->1---->3 3 v3---->0---->2 |
在以上建立邻接表的算法中,输入的顶点信息即为顶点的序号,因此,建立邻接表的时间复杂度为O(n+e)。
建立有向图的邻接表与此类似,只是更加简单,每当读入一个顶点对<i, j>时,仅需要生成一个邻接点序号为j的边表结点,将其插入到vi的出边表头即可。若要建立网络的邻接表,则需要在边表的每个结点中增加一个存储边上权值的数据域weight。
运行结果(有向图):
请输入图的顶点数和边数: 顶点 边。 4 5 请输入4个顶点的值。 v0 v1 v2 v3 请输入5条边: 顶点1 顶点2 请输入第1条边: v1 v3 请输入第2条边: v1 v2 请输入第3条边: v0 v3 请输入第4条边: v2 v3 请输入第5条边: v0 v2 有向图邻接表: 0 v0---->2---->3 1 v1---->2---->3 2 v2---->3 3 v3 |
封装
头文件ALGraph.h:
#pragma once #ifndef ADJACENCY_LIST_GRAPH_H #define ADJACENCY_LIST_GRAPH_H #include "linkQueue.h" #include "sequenceQueue.h" #include "serialStack.h" // Adjacency<əˈdʒeɪs(ə)nsi> struct ALGraph { struct AdjacencyListGraph* graph; void (*print)(struct ALGraph* aLGraph); void (*topology)(struct ALGraph* aLGraph); void (*depthFirstSearch)(struct ALGraph* aLGraph); void (*breadthFirstSearch)(struct ALGraph* aLGraph); int (*destroy)(struct ALGraph* aLGraph); }; // Please input NULL OR tell me the path, if write in hard disk. struct ALGraph* initALGraph(int isdigraph, char* path); struct ALGraph* readALGraphByDisk(int isdigraph, char* path); #endif |
实现逻辑ALGraph.c:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #define MODE 1 // 切换链式队列/顺序队列 #include "ALGraph.h" struct AdjacencyListGraph* createALGraph(); void writeALGraphInHardDisk(struct AdjacencyListGraph* graph, char* path); void printALGraph(struct ALGraph* graph); void topologyALGraph(struct ALGraph* graph); void depthFirstSearchALGraph(struct ALGraph* graph); void breadthFirstSearchALGraphSequence(struct ALGraph* graph); void breadthFirstSearchALGraphLink(struct ALGraph* graph); int destroyALGraph(struct ALGraph* graph); #define MaxVertex 50 int* digraphAL = -1; struct EdgeNode { int adjvex; struct EdgeNode* next; int weight; }; struct EdgeHeader { char data[sizeof(int)]; struct EdgeNode* firstEdge; int nodeSize; }; struct AdjacencyListGraph { struct EdgeHeader* vertex[MaxVertex]; int vertexNum; int edgeNum; }; struct ALGraph* initALGraphTail(struct ALGraph* aLGraph) { aLGraph->print = printALGraph; aLGraph->topology = topologyALGraph; aLGraph->depthFirstSearch = depthFirstSearchALGraph; aLGraph->breadthFirstSearch = MODE ? breadthFirstSearchALGraphLink : breadthFirstSearchALGraphSequence; aLGraph->destroy = destroyALGraph; return aLGraph; } struct ALGraph* initALGraph(int isdigraph, char* path) { if (isdigraph) { digraphAL = 1; } else { digraphAL = 0; } struct ALGraph* aLGraph = malloc(sizeof(struct ALGraph)); struct AdjacencyListGraph* graph = createALGraph(); if (aLGraph == NULL || graph == NULL) { printf("创建邻接表--%s图,分配内存空间失败!\n", digraphAL ? "有向" : "无向"); return NULL; } aLGraph->graph = graph; if (path != NULL) { writeALGraphInHardDisk(graph, path); } return initALGraphTail(aLGraph); } struct ALGraph* readALGraphByDisk(int isdigraph, char* path) { if (isdigraph) { digraphAL = 1; } else { digraphAL = 0; } if (path == NULL) { printf("邻接表--%s图,Input path can not be null.\n", digraphAL ? "有向" : "无向"); return NULL; } printf("邻接表--%s图读出磁盘%s保存的数据!\n", digraphAL ? "有向" : "无向", path); FILE* file = fopen(path, "rb+"); if (file == NULL) { printf("邻接表--%s图,Open file can not be null!\n", digraphAL ? "有向" : "无向"); return NULL; } struct ALGraph* aLGraph = malloc(sizeof(struct ALGraph)); struct AdjacencyListGraph* graph = malloc(sizeof(struct AdjacencyListGraph)); if (aLGraph == NULL || graph == NULL) { printf("读取邻接表--%s图,分配内存空间失败!\n", digraphAL ? "有向" : "无向"); return NULL; } aLGraph->graph = graph; fread(&graph->vertexNum, sizeof(int), 1, file); fread(&graph->edgeNum, sizeof(int), 1, file); for (int i = 0; i < graph->vertexNum; i++) { struct EdgeHeader* header = malloc(sizeof(struct EdgeHeader)); if (header == NULL) return NULL; graph->vertex[i] = header; fread(header, sizeof(struct EdgeHeader), 1, file); if (header->nodeSize > 0) { struct EdgeNode* edgeNode = malloc(sizeof(struct EdgeNode)); if (edgeNode == NULL) return NULL; header->firstEdge = edgeNode; fread(edgeNode, sizeof(struct EdgeNode), 1, file); } if (header->nodeSize > 1) { struct EdgeNode* edgeNode = header->firstEdge; for (int i = 1; i < header->nodeSize; i++) { struct EdgeNode* newNode = malloc(sizeof(struct EdgeNode)); if (newNode == NULL) return NULL; edgeNode->next = newNode; fread(newNode, sizeof(struct EdgeNode), 1, file); edgeNode = newNode; } } } fclose(file); return initALGraphTail(aLGraph); } int insertEdgeALGraph(struct AdjacencyListGraph* graph, int tailIndex, int headIndex, int weight) { struct EdgeNode* edgeNode = malloc(sizeof(struct EdgeNode)); if (edgeNode == NULL) { printf("邻接表--%s图结点空间分配失败!\n", digraphAL ? "有向" : "无向"); return 0; } edgeNode->adjvex = headIndex; edgeNode->next = NULL; edgeNode->weight = weight; if (graph->vertex[tailIndex]->firstEdge == NULL) { graph->vertex[tailIndex]->firstEdge = edgeNode; return 1; } struct EdgeNode* currentNode = graph->vertex[tailIndex]->firstEdge; struct EdgeNode* priorNode = graph->vertex[tailIndex]; // move the pointer while (currentNode->next != NULL && currentNode->adjvex < headIndex) { priorNode = currentNode; currentNode = currentNode->next; } // insert top if (headIndex < currentNode->adjvex) { priorNode->next = edgeNode; edgeNode->next = currentNode; } else if (headIndex > currentNode->adjvex) { // insert tail if (currentNode->next == NULL) { currentNode->next = edgeNode; } else { struct EdgeNode* nextNode = currentNode->next; currentNode->next = edgeNode; edgeNode->next = nextNode; } } else { return 0; } return 1; } int findVertexOfALGraph(struct AdjacencyListGraph* graph, char* vertex) { if (graph == NULL) { printf("邻接表--%s图不存在!\n", digraphAL ? "有向" : "无向"); return; } for (int i = 0; i < graph->vertexNum; i++) { if (strcmp(vertex, graph->vertex[i]->data) == 0) { return i; } } return INT_MAX; } struct AdjacencyListGraph* createALGraph() { struct AdjacencyListGraph* graph = malloc(sizeof(struct AdjacencyListGraph)); if (graph == NULL) { return; } printf("请输入%s图的顶点数和边数:\n顶点 边。\n", digraphAL ? "有向" : "无向"); scanf("%d", &graph->vertexNum); scanf("%d", &graph->edgeNum); printf("请输入%d个顶点的值。\n", graph->vertexNum); for (int i = 0; i < graph->vertexNum; i++) { struct EdgeHeader* vertex = malloc(sizeof(struct EdgeHeader)); if (vertex == NULL) { return; } graph->vertex[i] = vertex; scanf("%s", graph->vertex[i]->data); graph->vertex[i]->firstEdge = NULL; graph->vertex[i]->nodeSize = 0; } printf("请输入%d条边:\n%s\n", graph->edgeNum, digraphAL ? "弧尾 弧头 权重" : "顶点1 顶点2"); int weight = INT_MAX; char tail[8] = { 0 }, head[8] = { 0 }; for (int i = 1; i <= graph->edgeNum; i++) { printf("请输入第%d条边:\n", i); scanf("%s", tail); scanf("%s", head); if (digraphAL) { scanf("%d", &weight); } int tailIndex = findVertexOfALGraph(graph, tail); int headIndex = findVertexOfALGraph(graph, head); if (tailIndex == INT_MAX || headIndex == INT_MAX) { printf("输入的顶点不存在,请重新输入:\n"); i--; continue; } // insert edge, if true record it. if (insertEdgeALGraph(graph, tailIndex, headIndex, weight)) { graph->vertex[tailIndex]->nodeSize++; } // if undigraph add again. if (!digraphAL) { if (insertEdgeALGraph(graph, headIndex, tailIndex, weight)) { graph->vertex[headIndex]->nodeSize++; } } } return graph; } void writeALGraphInHardDisk(struct AdjacencyListGraph* graph, char* path) { if (graph == NULL) { printf("邻接表--%s图不存在!\n", digraphAL ? "有向" : "无向"); return; } printf("邻接表--%s图写入磁盘%s!\n", digraphAL ? "有向" : "无向", path); FILE* file = fopen(path, "wb+"); if (file == NULL) { printf("邻接表--%s图,Open file can not be null!\n", digraphAL ? "有向" : "无向"); return; } fwrite(&graph->vertexNum, sizeof(int), 1, file); fwrite(&graph->edgeNum, sizeof(int), 1, file); for (int i = 0; i < graph->vertexNum; i++) { struct EdgeHeader* header = graph->vertex[i]; fwrite(header, sizeof(struct EdgeHeader), 1, file); if (header->firstEdge != NULL) { struct EdgeNode* edgeNode = header->firstEdge; while (edgeNode != NULL) { fwrite(edgeNode, sizeof(struct EdgeNode), 1, file); edgeNode = edgeNode->next; } } } fclose(file); } void printALGraph(struct ALGraph* aLGraph) { if (aLGraph == NULL || aLGraph->graph == NULL) { printf("邻接表--%s图不存在!\n", digraphAL ? "有向" : "无向"); return; } struct AdjacencyListGraph* graph = aLGraph->graph; printf("邻接表--%s图结构:\n", digraphAL ? "有向" : "无向"); for (int i = 0; i < graph->vertexNum; i++) { printf("%d %s", i, graph->vertex[i]->data); if (graph->vertex[i]->firstEdge == NULL) { printf("\n"); continue; } struct EdgeNode* currentNode = graph->vertex[i]; do { currentNode = currentNode->next; if (digraphAL) { currentNode->weight == INT_MAX ? printf("---->%d ∞", currentNode->adjvex) : printf("---->%d %d", currentNode->adjvex, currentNode->weight); } else { printf("---->%d", currentNode->adjvex); } } while (currentNode->next != NULL); printf("\n"); } } void topologyALGraph(struct ALGraph* aLGraph) { if (aLGraph == NULL || aLGraph->graph == NULL) { printf("邻接表--%s图不存在!\n", digraphAL ? "有向" : "无向"); return; } struct AdjacencyListGraph* graph = aLGraph->graph; int size = graph->vertexNum; struct SequenceStack* stack = initSequenceStack(); int* inDegree = calloc(size, sizeof(int)); if (inDegree == NULL) { printf("邻接表--%s图拓扑排序开辟辅助数组内存不足!\n", digraphAL ? "有向" : "无向"); return; } for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { struct EdgeNode* firstEdge = graph->vertex[j]->firstEdge; while (firstEdge != NULL) { if (firstEdge->adjvex == i) { inDegree[i]++; } firstEdge = firstEdge->next; } } } for (int i = 0; i < size; i++) { if (inDegree[i] == 0) { int* index = malloc(sizeof(int)); if (index == NULL) return; *index = i; stack->push(index, stack); } } while (stack->isNotEmpty(stack)) { int* index = stack->top(stack); printf("顶点%s-->", graph->vertex[*index]->data); stack->pop(stack); struct EdgeNode* node = graph->vertex[*index]->firstEdge; struct EdgeNode* prior = NULL; free(index); while(node != NULL) { prior = node; node = node->next; inDegree[prior->adjvex]--; if (inDegree[prior->adjvex] == 0) { int* index = malloc(sizeof(int)); if (index == NULL) return; *index = prior->adjvex; stack->push(index, stack); } free(prior); prior = NULL; } } stack->destroy(stack); } void recursionALGraph(struct AdjacencyListGraph* graph, int* visited, int index) { printf("%s ", graph->vertex[index]->data); visited[index] = 1; struct EdgeHeader* edgeHeader = graph->vertex[index]; if (edgeHeader->firstEdge == NULL) { return; } struct EdgeNode* edgeNode = edgeHeader->firstEdge; while (edgeNode != NULL) { int next = edgeNode->adjvex; if (!visited[next]) { recursionALGraph(graph, visited, next); } edgeNode = edgeNode->next; } } void depthFirstSearchALGraph(struct ALGraph* aLGraph) { if (aLGraph == NULL || aLGraph->graph == NULL) { printf("邻接表--%s图不存在!\n", digraphAL ? "有向" : "无向"); return; } struct AdjacencyListGraph* graph = aLGraph->graph; printf("深度优先遍历邻接表--%s图!\n", digraphAL ? "有向" : "无向"); int* visited = malloc(sizeof(int) * (unsigned int)graph->vertexNum); if (visited == NULL) return; memset(visited, 0, sizeof(int) * (int)graph->vertexNum); for (int i = 0; i < graph->vertexNum; i++) { if (!visited[i]) { recursionALGraph(graph, visited, i); } } } void breadthSearchALGraphSequence(struct AdjacencyListGraph* graph, struct SequenceQueue* queue, int* visited, int index) { visited[index] = 1; int* num = malloc(sizeof(int)); if (num == NULL) return; *num = index; queue->push(num, queue); struct EdgeHeader* edgeHeader = graph->vertex[index]; printf("%s ", edgeHeader->data); while (queue->isNotEmpty(queue)) { int* front = queue->front(queue); struct EdgeHeader* edgeHeader = graph->vertex[*front]; if (edgeHeader->firstEdge == NULL) { queue->pop(queue); free(front); front = NULL; continue; } struct EdgeNode* edgeNode = edgeHeader->firstEdge; while (edgeNode != NULL) { if (!visited[edgeNode->adjvex]) { visited[edgeNode->adjvex] = 1; printf("%s ", graph->vertex[edgeNode->adjvex]->data); int* index = malloc(sizeof(int)); if (index == NULL) return; *index = edgeNode->adjvex; queue->push(index, queue); } edgeNode = edgeNode->next; } queue->pop(queue); free(front); front = NULL; } } void breadthFirstSearchALGraphSequence(struct ALGraph* aLGraph) { if (aLGraph == NULL || aLGraph->graph == NULL) { printf("邻接表--%s图不存在!\n", digraphAL ? "有向" : "无向"); return; } struct AdjacencyListGraph* graph = aLGraph->graph; printf("广度优先遍历邻接表--%s图:\n", digraphAL ? "有向" : "无向"); int* visited = malloc(sizeof(int) * (int)graph->vertexNum); if (visited == NULL) return; memset(visited, 0, sizeof(int) * (int)graph->vertexNum); struct SequenceQueue* queue = initSequenceQueue(); for (int i = 0; i < graph->vertexNum; i++) { if (!visited[i]) { breadthSearchALGraphSequence(graph, queue, visited, i); } } free(visited); visited = NULL; free(queue); queue = NULL; } struct Num { void* next; int index; }; void breadthSearchALGraphLink(struct AdjacencyListGraph* graph, struct LinkQueue* queue, int* visited, int index) { visited[index] = 1; struct Num* num = malloc(sizeof(struct Num)); if (num == NULL) return; num->index = index; queue->push(num, queue); printf("%s ", graph->vertex[index]->data); while (queue->isNotEmpty(queue)) { struct Num* front = queue->front(queue); struct EdgeHeader* edgeHeader = graph->vertex[front->index]; if (edgeHeader->firstEdge == NULL) { queue->pop(queue); free(front); front = NULL; continue; } struct EdgeNode* edgeNode = edgeHeader->firstEdge; while (edgeNode != NULL) { if (!visited[edgeNode->adjvex]) { visited[edgeNode->adjvex] = 1; printf("%s ", graph->vertex[edgeNode->adjvex]->data); struct Num* index = malloc(sizeof(struct Num)); if (index == NULL) return; index->index = edgeNode->adjvex; queue->push(index, queue); } edgeNode = edgeNode->next; } queue->pop(queue); free(front); front = NULL; } } void breadthFirstSearchALGraphLink(struct ALGraph* aLGraph) { if (aLGraph == NULL || aLGraph->graph == NULL) { printf("邻接表--%s图不存在!\n", digraphAL ? "有向" : "无向"); return; } struct AdjacencyListGraph* graph = aLGraph->graph; printf("广度优先遍历邻接表--%s图:\n", digraphAL ? "有向" : "无向"); int* visited = malloc(sizeof(int) * (int)graph->vertexNum); if (visited == NULL) return; memset(visited, 0, sizeof(int) * (int)graph->vertexNum); struct LinkQueue* queue = initLinkQueue(); for (int i = 0; i < graph->vertexNum; i++) { if (!visited[i]) { breadthSearchALGraphLink(graph, queue, visited, i); } } free(visited); visited = NULL; free(queue); queue = NULL; } int destroyALGraph(struct ALGraph* aLGraph) { if (aLGraph == NULL || aLGraph->graph == NULL) { printf("邻接表--%s图,不存在!\n", digraphAL ? "有向" : "无向"); return 0; } free(aLGraph->graph); free(aLGraph); aLGraph = NULL; return 1; } |
测试:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "ALGraph.h" int main() { struct ALGraph* graph = initALGraph(1, NULL); graph->print(graph); return 0; } |
运行结果:
请输入有向图的顶点数和边数: 顶点 边。 9 11 请输入9个顶点的值。 C1 C2 C3 C4 C5 C6 C7 C8 C9 请输入11条边: 弧尾 弧头 权重 请输入第1条边: C1 C7 1 请输入第2条边: C1 C4 1 请输入第3条边: C4 C3 1 请输入第4条边: C7 C3 1 请输入第5条边: C3 C8 1 请输入第6条边: C4 C8 1 请输入第7条边: C3 C5 1 请输入第8条边: C2 C7 1 请输入第9条边: C2 C9 1 请输入第10条边: C9 C6 1 请输入第11条边: C6 C5 1 邻接表--有向图结构: 0 C1---->3 1---->6 1 1 C2---->6 1---->8 1 2 C3---->4 1---->7 1 3 C4---->2 1---->7 1 4 C5 5 C6---->4 1 6 C7---->2 1 7 C8 8 C9---->5 1 |
6.2.3.十字链表
十字链表(Orthogonal List)是有向图的另一种链式存储结构。我们也可以把它看成是将有向图的邻接表和逆邻接表结合起来形成的一种链表。
有向图中的每一条弧对应十字链表中的弧结点,同时有向图中的每个顶点在十字链表中对应有一个结点,叫做顶点结点。
构建十字链表,先按照邻接表的构造方法构建一个出边,再找到这条边的入边端点,用指针指向这条边,便构成了十字链表。十字链表的数据存储空间与邻接表几乎一样大,只多了一些指向入边弧的指针。
十字链表数据存储模型在邻接表的基础上稍稍做了修改:
static struct EdgeNode { int tailVertex; struct EdgeNode* tailLink; int heahVertex; struct EdgeNode* heahLink; int weight; }; struct VertexHeader { char data[4]; struct EdgeNode* firstEdgeIn; struct EdgeNode* firstEdgeOut; int outNodeSize; }; struct OrthogonalListGraph { struct VertexHeader** vertex; int vertexNum; int edgeNum; }; |
新建头文件OLGraph.h:
#pragma once #ifndef ORTHOGONAL_LIST_GRAPH #define ORTHOGONAL_LIST_GRAPH #include "linkQueue.h" // ɔːˈθɒɡən(ə)l Orthogonal lList struct OLGraph { struct OrthogonalListGraph* graph; void (*print)(struct OLGraph* oLGraph); void (*topology)(struct OLGraph* oLGraph); void (*keyPath)(struct OLGraph* oLGraph); void(*destroy)(struct OLGraph* oLGraph); }; struct OLGraph* initOLGraph(); #endif |
实现头文件逻辑OLGraph.c:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "OLGraph.h" #define PRINT 1 struct OrthogonalListGraph* createOLGraph(); void printOLGraph(struct OLGraph* oLGraph); void topologyOLGraph(struct OLGraph* oLGraph); void keyPathOLGraph(struct OLGraph* oLGraph); void destroyOLGraph(struct OLGraph* oLGraph); struct OLGraph* initOLGraph() { struct OLGraph* oLGraph = malloc(sizeof(struct OLGraph)); if (oLGraph == NULL) { #ifdef PRINT printf("初始化十字链表失败,内存空间不足!"); #endif return NULL; } oLGraph->graph = createOLGraph(); oLGraph->print = printOLGraph; oLGraph->topology = topologyOLGraph; oLGraph->keyPath = keyPathOLGraph; oLGraph->destroy = destroyOLGraph; return oLGraph; } static struct EdgeNode { int index; char* edge; int tailVertex; struct EdgeNode* tailLink; int heahVertex; struct EdgeNode* heahLink; int weight; }; static struct VertexHeader { char data[4]; struct EdgeNode* firstEdgeIn; struct EdgeNode* firstEdgeOut; int outNodeSize; }; struct OrthogonalListGraph { struct VertexHeader** vertex; struct EdgeNode** edge; int vertexNum; int edgeNum; }; struct OrthogonalListGraph* createOLGraph() { struct OrthogonalListGraph* graph = malloc(sizeof(struct OrthogonalListGraph)); if (graph == NULL) { #ifdef PRINT printf("初始化十字链表失败,内存空间不足!"); #endif return NULL; } printf("请输入有向图的顶点数和边数:\n顶点 边。\n"); scanf("%d", &graph->vertexNum); scanf("%d", &graph->edgeNum); struct VertexHeader** vertex = malloc(sizeof(struct VertexHeader*) * graph->vertexNum); struct EdgeNode** edge = malloc(sizeof(struct EdgeNode*) * graph->edgeNum); if (vertex == NULL || edge == NULL) { #ifdef PRINT printf("创建十字链表顶点失败,内存空间不足!"); #endif return NULL; } graph->vertex = vertex; graph->edge = edge; printf("请输入%d个顶点的值。\n", graph->vertexNum); for (int i = 0; i < graph->vertexNum; i++) { struct VertexHeader* header = malloc(sizeof(struct VertexHeader)); if (header == NULL) { return; } graph->vertex[i] = header; scanf("%s", &header->data); header->firstEdgeIn = NULL; header->firstEdgeOut = NULL; header->outNodeSize = 0; } void initOLGraphFirstEdgeOut(struct OrthogonalListGraph* graph); initOLGraphFirstEdgeOut(graph); return graph; } void initOLGraphFirstEdgeOut(struct OrthogonalListGraph* graph) { printf("请输入%d条边:\n弧尾 弧头 边 权重\n", graph->edgeNum); int weight = INT_MAX; char tail[4] = { 0 }, head[4] = { 0 }; for (int i = 1; i <= graph->edgeNum; i++) { printf("请输入第%d条边:\n", i); scanf("%s", tail); scanf("%s", head); char* edge = malloc(sizeof(char) * 4); if (edge == NULL) return; scanf("%s", edge); scanf("%d", &weight); int findVertexOfOLGraph(struct OrthogonalListGraph* graph, char* vertex); int tailIndex = findVertexOfOLGraph(graph, tail); int headIndex = findVertexOfOLGraph(graph, head); if (tailIndex == INT_MAX || headIndex == INT_MAX) { printf("输入的顶点不存在,请重新输入:\n"); i--; continue; } int insertOLGraphOutEdge(struct OrthogonalListGraph* graph, int tailIndex, int headIndex, int weight, char* edge, struct EdgeNode** node, int index); struct EdgeNode* edgeNode = NULL; if (insertOLGraphOutEdge(graph, tailIndex, headIndex, weight, edge, &edgeNode, i)) { graph->vertex[tailIndex]->outNodeSize++; void initOLGraphFirstEdgeIn(struct OrthogonalListGraph* graph, int tailIndex, int headIndex, struct EdgeNode* edgeNode); initOLGraphFirstEdgeIn(graph, tailIndex, headIndex, edgeNode); graph->edge[i - 1] = edgeNode; } else { printf("新增弧信息失败,请重新输入:\n"); i--; } } } int findVertexOfOLGraph(struct OrthogonalListGraph* graph, char* vertex) { if (graph == NULL) { #ifdef PRINT printf("十字链表--有向图不存在!\n"); #endif return INT_MAX; } for (int i = 0; i < graph->vertexNum; i++) { if (strcmp(vertex, graph->vertex[i]->data) == 0) { return i; } } return INT_MAX; } int insertOLGraphOutEdge(struct OrthogonalListGraph* graph, int tailIndex, int headIndex, int weight, char* edge, struct EdgeNode** node, int index) { struct EdgeNode* edgeNode = malloc(sizeof(struct EdgeNode)); *node = edgeNode; if (edgeNode == NULL) { #ifdef PRINT printf("十字链表--有向图邻接结点空间分配失败!\n"); #endif return 0; } edgeNode->index = index; edgeNode->edge = edge; edgeNode->tailVertex = tailIndex; edgeNode->tailLink = NULL; edgeNode->heahVertex = headIndex; edgeNode->heahLink = NULL; edgeNode->weight = weight; if (graph->vertex[tailIndex]->firstEdgeOut == NULL) { graph->vertex[tailIndex]->firstEdgeOut = edgeNode; return 1; } struct EdgeNode* currentNode = graph->vertex[tailIndex]->firstEdgeOut; struct EdgeNode* priorNode = NULL; // move the pointer while (currentNode->tailLink != NULL && headIndex > currentNode->heahVertex) { priorNode = currentNode; currentNode = currentNode->tailLink; } // insert top if (headIndex < currentNode->heahVertex) { if (priorNode == NULL) { graph->vertex[tailIndex]->firstEdgeOut = edgeNode; } else { priorNode->tailLink = edgeNode; } edgeNode->tailLink = currentNode; } else if (headIndex > currentNode->heahVertex) { currentNode->tailLink = edgeNode; } else { free(edgeNode); edgeNode = NULL; *node = NULL; printf("弧尾%d 弧头%d 已存在,更新权值:%d 名称:%s\n", tailIndex, headIndex, weight, edge); currentNode->weight = weight; currentNode->edge = edge; return 0; } return 1; } void initOLGraphFirstEdgeIn(struct OrthogonalListGraph* graph, int tailIndex, int headIndex, struct EdgeNode* edgeNode) { if (graph->vertex[headIndex]->firstEdgeIn == NULL) { graph->vertex[headIndex]->firstEdgeIn = edgeNode; return; } struct EdgeNode* currentNode = graph->vertex[headIndex]->firstEdgeIn; struct EdgeNode* priorNode = NULL; // move the pointer while (currentNode->heahLink != NULL && tailIndex > currentNode->tailLink) { priorNode = currentNode; currentNode = currentNode->heahLink; } // insert top if (tailIndex < currentNode->tailLink) { if (priorNode == NULL) { graph->vertex[headIndex]->firstEdgeIn = edgeNode; } else { priorNode->heahLink = edgeNode; } edgeNode->heahLink = currentNode; } else if (tailIndex > currentNode->tailLink) { currentNode->heahLink = edgeNode; } } void printOLGraph(struct OLGraph* oLGraph) { if (oLGraph == NULL || oLGraph->graph == NULL) { #ifdef PRINT printf("十字链表不存在!"); #endif return NULL; } void printOLGraphEdgeOut(struct OLGraph* oLGraph); printOLGraphEdgeOut(oLGraph); void printOLGraphEdgeIn(struct OLGraph* oLGraph); printOLGraphEdgeIn(oLGraph); } void printOLGraphEdgeOut(struct OLGraph* oLGraph) { struct OrthogonalListGraph* graph = oLGraph->graph; printf("十字链表图结构->出边表:\n"); for (int i = 0; i < graph->vertexNum; i++) { printf("%d %s", i, graph->vertex[i]->data); if (graph->vertex[i]->firstEdgeOut == NULL) { printf("\n"); continue; } struct EdgeNode* currentNode = graph->vertex[i]->firstEdgeOut; do { printf("====[%d->%d %d]", currentNode->tailVertex, currentNode->heahVertex, currentNode->weight); currentNode = currentNode->tailLink; } while (currentNode != NULL); printf("\n"); } } void printOLGraphEdgeIn(struct OLGraph* oLGraph) { struct OrthogonalListGraph* graph = oLGraph->graph; printf("十字链表图结构->入边表:\n"); for (int i = 0; i < graph->vertexNum; i++) { printf("%d %s", i, graph->vertex[i]->data); if (graph->vertex[i]->firstEdgeIn == NULL) { printf("\n"); continue; } struct EdgeNode* currentNode = graph->vertex[i]->firstEdgeIn; do { printf("====[%d->%d %d]", currentNode->tailVertex, currentNode->heahVertex, currentNode->weight); currentNode = currentNode->heahLink; } while (currentNode != NULL); printf("\n"); } } void topologyOLGraph(struct OLGraph* oLGraph) { if (oLGraph == NULL || oLGraph->graph == NULL) { #ifdef PRINT printf("十字链表不存在!"); #endif return; } struct OrthogonalListGraph* graph = oLGraph->graph; int size = graph->vertexNum; for (int i = 0; i < size; i++) { if (graph->vertex[i]->firstEdgeIn == NULL && graph->vertex[i]->outNodeSize > 0) { printf("顶点%s-->", graph->vertex[i]->data); struct EdgeNode* firstOut = graph->vertex[i]->firstEdgeOut; struct EdgeNode* prior = NULL; graph->vertex[i]->firstEdgeOut = NULL; while (firstOut != NULL) { prior = firstOut; firstOut = firstOut->tailLink; prior->tailLink = NULL; // 处理入边表 struct EdgeNode* firstIn = graph->vertex[prior->heahVertex]->firstEdgeIn; struct EdgeNode* priorNode = NULL; if (firstIn != NULL && prior == firstIn) { graph->vertex[prior->heahVertex]->firstEdgeIn = firstIn->heahLink; if (graph->vertex[prior->heahVertex]->firstEdgeOut == NULL && graph->vertex[prior->heahVertex]->firstEdgeIn == NULL) { printf("顶点%s-->", graph->vertex[prior->heahVertex]->data); } free(firstIn); firstIn = NULL; } while (firstIn != NULL) { priorNode = firstIn; firstIn = firstIn->heahLink; if (prior == firstIn) { priorNode->heahLink = firstIn->heahLink; free(firstIn); firstIn = NULL; } } } graph->vertex[i]->outNodeSize = 0; i = -1; } } } struct VertexTime { char* vertex; int vertexEarly; int vertexLate; }; struct EdgeTime { char* edge; int early; int late; int timeMargin; }; void keyPathOLGraph(struct OLGraph* oLGraph) { if (oLGraph == NULL || oLGraph->graph == NULL) { #ifdef PRINT printf("十字链表不存在!"); #endif return; } struct OrthogonalListGraph* graph = oLGraph->graph; int size = graph->vertexNum; int sourceVertex = -1, convergeVertex = -1, count = 0; for (int i = 0; i < size; i++) { if (graph->vertex[i]->firstEdgeIn == NULL) { sourceVertex = i; count++; } if (graph->vertex[i]->firstEdgeOut == NULL) { convergeVertex = i; count++; } } if (count > 2 && sourceVertex != -1 && convergeVertex != -1) { printf("十字链表中源点与汇点不唯一!\n"); } struct VertexTime* vertexArray = calloc(size, sizeof(struct VertexTime)); struct EdgeTime* edgeArray = malloc(sizeof(struct EdgeTime) * graph->edgeNum); if (vertexArray == NULL || edgeArray == NULL) { printf("求关键路径,内存空间不足!\n"); return; } for (int i = 0; i < size; i++) { vertexArray[i].vertexEarly = 0; vertexArray[i].vertexLate = INT_MAX; } void createVertexEarlyBreadth(struct OrthogonalListGraph* graph, struct VertexTime* vertexArray, int sourceVertex); createVertexEarlyBreadth(graph, vertexArray, sourceVertex); vertexArray[convergeVertex].vertexLate = vertexArray[convergeVertex].vertexEarly; void createVertexLateBreadth(struct OrthogonalListGraph* graph, struct VertexTime* vertexArray, int convergeVertex); createVertexLateBreadth(graph, vertexArray, convergeVertex); void createEdgeLateBreadth(struct OrthogonalListGraph* graph, struct VertexTime* vertexArray, struct EdgeTime* edgeArray); createEdgeLateBreadth(graph, vertexArray, edgeArray); printf("事件:\n"); for (int i = 0; i < size; i++) { printf("顶点%s ", vertexArray[i].vertex); printf("%d ", vertexArray[i].vertexEarly); printf("%d \n", vertexArray[i].vertexLate); } printf("活动:\n"); for (int i = 0; i < graph->edgeNum; i++) { printf("弧%s ", edgeArray[i].edge); printf("early(i)=%d ", edgeArray[i].early); printf("late(i)=%d ", edgeArray[i].late); printf("late(i)-early(i)=%d \n", edgeArray[i].timeMargin); } } struct PathIndex { void* pointer; int index; }; void createVertexEarlyBreadth(struct OrthogonalListGraph* graph, struct VertexTime* vertexArray, int sourceVertex) { struct LinkQueue* queue = initLinkQueue(); struct PathIndex* pathIndex = malloc(sizeof(struct PathIndex)); if (pathIndex == NULL) return; pathIndex->pointer = NULL; pathIndex->index = sourceVertex; queue->push(pathIndex, queue); while (queue->isNotEmpty(queue)) { struct PathIndex* pathIndex = queue->front(queue); struct EdgeNode* edgeOut = graph->vertex[pathIndex->index]->firstEdgeOut; struct EdgeNode* prior = NULL; while (edgeOut != NULL) { prior = edgeOut; edgeOut = edgeOut->tailLink; int vertexEarly = vertexArray[prior->tailVertex].vertexEarly + prior->weight; if (vertexEarly > vertexArray[prior->heahVertex].vertexEarly) { vertexArray[prior->heahVertex].vertexEarly = vertexEarly; vertexArray[prior->heahVertex].vertex = graph->vertex[prior->heahVertex]->data; }
struct PathIndex* vIndex = malloc(sizeof(struct PathIndex)); if (vIndex == NULL) return; vIndex->index = prior->heahVertex; int comparePathIndex(void* first, void* second); if (queue->isNotExist(vIndex, comparePathIndex, queue)) { queue->push(vIndex, queue); } } queue->pop(queue); } queue->destroy(queue); } int comparePathIndex(void* first, void* second) { struct PathIndex* firstIndex = first; struct PathIndex* secondIndex = second; if (firstIndex->index == secondIndex->index) { return 1; } return 0; } void createVertexLateBreadth(struct OrthogonalListGraph* graph, struct VertexTime* vertexArray, int convergeVertex) { struct LinkQueue* queue = initLinkQueue(); struct PathIndex* pathIndex = malloc(sizeof(struct PathIndex)); if (pathIndex == NULL) return; pathIndex->pointer = NULL; pathIndex->index = convergeVertex; queue->push(pathIndex, queue); while (queue->isNotEmpty(queue)) { struct PathIndex* pathIndex = queue->front(queue); struct EdgeNode* edgeIn = graph->vertex[pathIndex->index]->firstEdgeIn; struct EdgeNode* prior = NULL; while (edgeIn != NULL) { prior = edgeIn; edgeIn = edgeIn->heahLink; int vertexLate = vertexArray[prior->heahVertex].vertexLate - prior->weight; if (vertexLate < vertexArray[prior->tailVertex].vertexLate) { vertexArray[prior->tailVertex].vertexLate = vertexLate; } struct PathIndex* vIndex = malloc(sizeof(struct PathIndex)); if (vIndex == NULL) return; vIndex->index = prior->tailVertex; if (queue->isNotExist(vIndex, comparePathIndex, queue)) { queue->push(vIndex, queue); } } queue->pop(queue); } queue->destroy(queue); } void createEdgeLateBreadth(struct OrthogonalListGraph* graph, struct VertexTime* vertexArray, struct EdgeTime* edgeArray) { int size = graph->edgeNum; for (int i = 0; i < size; i++) { struct EdgeNode* edge = graph->edge[i]; edgeArray[i].edge = edge->edge; edgeArray[i].early = vertexArray[edge->tailVertex].vertexEarly; edgeArray[i].late = vertexArray[edge->heahVertex].vertexLate - edge->weight; edgeArray[i].timeMargin = edgeArray[i].late - edgeArray[i].early; } } void destroyOLGraph(struct OLGraph* oLGraph) { if (oLGraph == NULL || oLGraph->graph == NULL) { #ifdef PRINT printf("十字链表不存在!"); #endif return; } struct VertexHeader** vertex = oLGraph->graph->vertex; int size = oLGraph->graph->vertexNum; for (int i = 0; i < size; i++) { struct EdgeNode* first = vertex[i]->firstEdgeOut; if (first == NULL) { continue; } struct EdgeNode* next = NULL; while (first->tailLink != NULL) { next = first->tailLink; free(first); first = next; } free(first); first = NULL; free(vertex[i]); vertex[i] = NULL; } free(oLGraph->graph); oLGraph->graph = NULL; free(oLGraph); oLGraph = NULL; } |
测试:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "OLGraph.h" int main() { struct OLGraph* graph = initOLGraph(); graph->print(graph); graph->destroy(graph); return 0; } |
运行结果:
请输入有向图的顶点数和边数: 顶点 边。 4 5 请输入4个顶点的值。 V0 V1 V2 V3 请输入5条边: 弧尾 弧头 权重 请输入第1条边: V0 V1 12 请输入第2条边: V0 V2 24 请输入第3条边: V1 V0 22 请输入第4条边: V2 V0 54 请输入第5条边: V2 V3 54 十字链表图结构->出边表: 0 V0====[0->1 12]====[0->2 24] 1 V1====[1->0 22] 2 V2====[2->0 54]====[2->3 54] 3 V3 十字链表图结构->入边表: 0 V0====[1->0 22]====[2->0 54] 1 V1====[0->1 12] 2 V2====[0->2 24] 3 V3====[2->3 54] |
6.2.4.邻接多重表
邻接多重表是无向图的压缩链式存储结构。
邻接表优点:容易求得顶点和边的信息。
缺点:某些操作不方便,如删除无向图一条边需找到表示此边的两个结点。
邻接多重表存储结构与邻接表存储边结点的结构有所不同,其它部分存储结构类似。边结点增加了两个结点在数组中存储的坐标索引,同时为这两个结点增加了指向下一个边结点的指针。
邻接多重表数据存储结构同样在邻接表的基础上左少许修改:
static struct EdgeNode { int mark; int vertex; struct EdgeNode* vertexLink; int adjvex; struct EdgeNode* adjvexLink; int weight; }; static struct VertexHeader { char data[4]; struct EdgeNode* firstEdge; int nodeSize; }; struct AdjacencyMultilist { struct VertexHeader** vertex; int vertexNum; int edgeNum; }; |
构建文件头AMultilist.h定义邻接多重表的基本功能:
#pragma once #ifndef ADJACENCY_MULTILIST_H #define ADJACENCY_MULTILIST_H #include "dynamicArray.h" // Adjacency[əˈdʒeɪsənsi] Multilist struct AMultilist { struct AdjacencyMultilist* multilist; void (*print)(struct AMultilist* aMultilist); void(*destroy)(struct AMultilist* aMultilist); }; struct AMultilist* initAMultilist(); #endif |
实现头文件逻辑AMultilist.c,构建邻接多重表,每新增一个边先记录到顶点的链表后面,再找到邻接点的链表并将邻接点的尾指针指向新建的边。邻接多重表的销毁借助了动态数组(2.2.2小节)ArrayList先遍历记录(遍历到的边mark标记一下)邻接表的边,然后利用ArrayList的ruin函数销毁内存:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "AMultilist.h" #define PRINT 1 struct AdjacencyMultilist* createAMultilist(); void printAdjacencyMultilist(struct AMultilist* aMultilist); void destroyAdjacencyMultilist(struct AMultilist* aMultilist); struct AMultilist* initAMultilist() { struct AMultilist* aMultilist = malloc(sizeof(struct AMultilist)); if (aMultilist == NULL) { #ifdef PRINT printf("初始化邻接多重表失败,内存空间不足!"); #endif return NULL; } aMultilist->multilist = createAMultilist(); aMultilist->print = printAdjacencyMultilist; aMultilist->destroy = destroyAdjacencyMultilist; } static struct EdgeNode { int mark; int vertex; struct EdgeNode* vertexLink; int adjvex; struct EdgeNode* adjvexLink; int weight; }; static struct VertexHeader { char data[4]; struct EdgeNode* firstEdge; int nodeSize; }; struct AdjacencyMultilist { struct VertexHeader** vertex; int vertexNum; int edgeNum; }; struct AdjacencyMultilist* createAMultilist() { struct AdjacencyMultilist* multilist = malloc(sizeof(struct AdjacencyMultilist)); if (multilist == NULL) { #ifdef PRINT printf("创建邻接多重表失败,内存空间不足!"); #endif return NULL; } printf("请输入无向图的顶点数和边数:\n顶点 边。\n"); scanf("%d", &multilist->vertexNum); scanf("%d", &multilist->edgeNum); struct VertexHeader** vertex = malloc(sizeof(struct VertexHeader*) * multilist->vertexNum); if (vertex == NULL) { #ifdef PRINT printf("创建邻接多重表存储结构失败,内存空间不足!"); #endif return NULL; } multilist->vertex = vertex; printf("请输入%d个顶点的值。\n", multilist->vertexNum); for (int i = 0; i < multilist->vertexNum; i++) { struct VertexHeader* header = malloc(sizeof(struct VertexHeader)); if (header == NULL) { #ifdef PRINT printf("创建邻接多重表存储顶点失败,内存空间不足!"); #endif return; } vertex[i] = header; scanf("%s", &header->data); header->firstEdge = NULL; // header->firstMarkEdge = NULL; header->nodeSize = 0; } void initMultilistFirstEdge(struct AdjacencyMultilist* multilist); initMultilistFirstEdge(multilist); return multilist; } void initMultilistFirstEdge(struct AdjacencyMultilist* multilist) { printf("请输入%d条边:\n顶点 邻接点\n", multilist->edgeNum); int weight = INT_MAX; char vertex[4] = { 0 }, adjvex[4] = { 0 }; for (int i = 1; i <= multilist->edgeNum; i++) { printf("请输入第%d条边:\n", i); scanf("%s", vertex); scanf("%s", adjvex); scanf("%d", &weight); int findVertexOfMultilist(struct AdjacencyMultilist* multilist, char* vertex); int vertexIndex = findVertexOfMultilist(multilist, vertex); int adjvexIndex = findVertexOfMultilist(multilist, adjvex); if (vertexIndex == INT_MAX || adjvexIndex == INT_MAX) { printf("输入的顶点不存在,请重新输入:\n"); i--; continue; } if (vertexIndex == adjvexIndex) { printf("输入的顶点不能指向自身,请重新输入:\n"); i--; continue; } int insertEdgeMultilist(struct AdjacencyMultilist* multilist, int vertexIndex, int adjvexIndex, int weight, struct EdgeNode** node); struct EdgeNode* edgeNode = NULL; if (insertEdgeMultilist(multilist, vertexIndex, adjvexIndex, weight, &edgeNode)) { multilist->vertex[vertexIndex]->nodeSize++; insertEdgeMultilist(multilist, adjvexIndex, vertexIndex, weight, &edgeNode); } else { printf("新增边信息失败,请重新输入:\n"); i--; } } } int findVertexOfMultilist(struct AdjacencyMultilist* multilist, char* vertex) { if (multilist == NULL || multilist->vertex == NULL) { #ifdef PRINT printf("邻接多重表--无向图不存在!\n"); #endif return INT_MAX; } for (int i = 0; i < multilist->vertexNum; i++) { if (strcmp(vertex, multilist->vertex[i]->data) == 0) { return i; } } return INT_MAX; } int insertEdgeMultilist(struct AdjacencyMultilist* multilist, int vertexIndex, int adjvexIndex, int weight, struct EdgeNode** node) { if (*node == NULL) { struct EdgeNode* edgeNode = malloc(sizeof(struct EdgeNode)); *node = edgeNode; if (edgeNode == NULL) { #ifdef PRINT printf("邻接多重表--无向图邻接结点空间分配失败!\n"); #endif return 0; } edgeNode->vertex = vertexIndex; edgeNode->vertexLink = NULL; edgeNode->adjvex = adjvexIndex; edgeNode->adjvexLink = NULL; edgeNode->weight = weight; } if (multilist->vertex[vertexIndex]->firstEdge == NULL) { multilist->vertex[vertexIndex]->firstEdge = *node; return 1; } struct EdgeNode* currentNode = multilist->vertex[vertexIndex]->firstEdge; struct EdgeNode* priorNode = NULL; while (currentNode != NULL) { if ((currentNode->vertex == vertexIndex && currentNode->adjvex == adjvexIndex) || (currentNode->adjvex == vertexIndex && currentNode->vertex == adjvexIndex)) { printf("顶点%d 邻接点%d 已存在,更新权值为:%d\n", vertexIndex, adjvexIndex, weight); currentNode->weight = weight; return 0; } if (currentNode->vertex == vertexIndex) { priorNode = currentNode; currentNode = currentNode->vertexLink; } else { priorNode = currentNode; currentNode = currentNode->adjvexLink; } } if (priorNode->vertex == vertexIndex) { priorNode->vertexLink = *node; } else { priorNode->adjvexLink = *node; } return 1; } void printAdjacencyMultilist(struct AMultilist* aMultilist) { if (aMultilist == NULL || aMultilist->multilist == NULL) { #ifdef PRINT printf("邻接多重表--无向图不存在!\n"); #endif return INT_MAX; } struct AdjacencyMultilist* multilist = aMultilist->multilist; printf("邻接多重表图结构->顶点到邻接点:\n"); for (int i = 0; i < multilist->vertexNum; i++) { printf("%d %s", i, multilist->vertex[i]->data); if (multilist->vertex[i]->firstEdge == NULL) { printf("\n"); continue; } struct EdgeNode* currentNode = multilist->vertex[i]->firstEdge; do { printf("====[%d->%d %d]", currentNode->vertex, currentNode->adjvex, currentNode->weight); if (currentNode->vertex == i) { currentNode = currentNode->vertexLink; } else { currentNode = currentNode->adjvexLink; } } while (currentNode != NULL); printf("\n"); } } void destroyAdjacencyMultilist(struct AMultilist* aMultilist) { if (aMultilist == NULL || aMultilist->multilist == NULL) { #ifdef PRINT printf("邻接多重表--无向图不存在!\n"); #endif return INT_MAX; } struct ArrayList* arrayList = initArrayList(5); struct AdjacencyMultilist* multilist = aMultilist->multilist; int size = multilist->vertexNum; struct VertexHeader** vertex = multilist->vertex; for (int i = 0; i < size; i++) { struct EdgeNode* edgeNode = vertex[i]->firstEdge; while (edgeNode != NULL) { if (edgeNode->vertex == i) { if (edgeNode->mark != 1) { arrayList->insert(0, edgeNode, arrayList); } edgeNode->mark = 1; edgeNode = edgeNode->vertexLink; } else { if (edgeNode->mark != 1) { arrayList->insert(0, edgeNode, arrayList); } edgeNode->mark = 1; edgeNode = edgeNode->adjvexLink; } } free(vertex[i]); vertex[i] = NULL; } arrayList->ruin(arrayList); free(vertex); vertex = NULL; free(multilist); multilist = NULL; free(aMultilist); aMultilist = NULL; } |
测试:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "AMultilist.h" int main() { struct AMultilist* aMultilist = initAMultilist(); aMultilist->print(aMultilist); aMultilist->destroy(aMultilist); return 0; } |
运行结果:
请输入无向图的顶点数和边数: 顶点 边。 4 5 请输入4个顶点的值。 V0 V1 V2 V3 请输入5条边: 顶点 邻接点 请输入第1条边: V0 V1 2 请输入第2条边: V0 V2 5 请输入第3条边: V1 V2 54 请输入第4条边: V2 V3 5 请输入第5条边: V1 V3 6 邻接多重表图结构->顶点到邻接点: 0 V0====[0->1 2]====[0->2 5] 1 V1====[0->1 2]====[1->2 54]====[1->3 6] 2 V2====[0->2 5]====[1->2 54]====[2->3 5] 3 V3====[2->3 5]====[1->3 6] |
运行结果虽然打印出来10条边,但是存储结构上只存储了5条边,多出来的5条边是利用指针重复标记实现的。