假设一个图的顶点数是V,则用 大小的二维数组来表示这个图边的信息,例如矩阵arcs[i][j],数值为1表示从下标为i的顶点到下标为j的顶点有边,数值为0表示从下标为i的顶点到下标为j的顶点没有边。矩阵的对角线上数值默认是0,因为没有自己顶点到自己顶点的边。
#include
#include
#include
#define MAX 100
#define INFINITY 9999
enum graphType {DG, UG, DN, UN}; //图的类型定义:有向图,无向图,有向网,无项网
typedef char vertexType;
typedef struct {
vertexType vexs[MAX];
int arcs[MAX][MAX];
int vexnum, arcnum;
graphType kind;
} graph;
预定义MAX的值为100,表示二维数组最多能够存储100个顶点之间边的关系,同时也表示这个图最多能够存储100个节点。
预定义INFINITY的值为9999,表示边长权值为无无限,即边关系为不存在。
enum是一个枚举数据类型,花括号括起来的是枚举的元素,“DG, UG, DN, UN”分别表示有向图,无向图,有向网,无向网。
vertexType vexs[MAX]定义一个MAX大小的一维数组,存放的是顶点的名称数据vexs是vertex的复数缩写,vertex的意思是顶点,例如vexs数组可以存放不同的城市的名称,不同城市之间的距离可以用边长的权值表示。
arcs[MAX][MAX]定义一个MAX大小的方阵,即二维数组,长和宽都是MAX大小,arcs[i][j]表示下标i对应的顶点到下标j对应的顶点的权值,arcs是弧长的意思。
vexnum, arcnum分别是顶点的个数,边的个数。
kind是这个图的类型定义。
void initGraph(graph &g) {
g.kind = UN;
printf("输入顶点数和边数:\n");
scanf("%d%d", &g.vexnum, &g.arcnum);
for (int i = 1; i <= g.vexnum; i++) {
g.vexs[i] = i;
}
for (int i = 1; i <= g.vexnum; i++) {
for (int j = 1; j < g.vexnum; j++) {
if (i == j) g.arcs[i][j] = 0;
else g.arcs[i][j] = INFINITY;
}
}
}
initGraph(graph &g)语句中,运用了C++中引用的技巧,在参数的前面添加“&”这个符号表示引用,引用的意思是,函数内变量g与外界传入进来的g是一个共同体,函数内g的任何改变都会影响外界传入的g,也就是函数中的参数g是外界传入的g的不同展现形式,本质上是相同的变量,这样我们就不用传入地址,根据地址去寻找本体了。
g.kind = UN把这个图的类型定义为无向图。
分别输入图的顶点个数和边的个数。
for (int i = 1; i <= g.vexnum; i++) { g.vexs[i] = i; }语句,定义每一个顶点的名称,我们这里就直接用1、2、3...这样的数字去定义,注意我们在代码实现过程中,舍弃了数组中第一个元素,所以我们数组能够存储的数据的最大值变成了99,这样做可以使代码变得更清晰一点。
for (int i = 1; i <= g.vexnum; i++) { for (int j = 1; j < g.vexnum; j++) { if (i == j) g.arcs[i][j] = 0; else g.arcs[i][j] = INFINITY; } }语句,把存储边长的数组对角线元素全部置0,对角线的含义是每一个顶点,自己到自己的权值,当然,这个值应该为0,因为没有意义。如果不是对角线,置为无穷,表示目前还没有这条边存在的关系。
void createGraph(graph &g) {
int start_index, end_index, weight;
printf("输入每条边的起点终点下标和边的权重:\n");
for (int i = 1; i <= g.arcnum; i++) {
scanf("%d%d%d", &start_index, &end_index, &weight);
g.arcs[start_index][end_index] = weight;
g.arcs[end_index][start_index] = weight;
}
}
因为我们在初始化函数中,存储了图顶点的信息,建图函数中只要实现边信息的存储就可以了。
start_index, end_index, weight分别表示起始顶点下标,终点下标,和这条边的权重。
我们表示不同的顶点,都是通过它的下标。
g.arcs[start_index][end_index] = weight; g.arcs[end_index][start_index] = weight;
因为我们表示的是无向图,所以起点到终点有权重,终点到起点同样有权重。
void showGraph(graph &g) {
printf("邻接矩阵:\n");
for (int i = 1; i <= g.vexnum; i++) {
for (int j = 1; j <= g.vexnum; j++) {
printf("%d ", g.arcs[i][j]);
}
printf("\n");
}
}
遍历二维数组,把二维数组中的每一个值都打印出来。
int main() {
graph g;
initGraph(g);
createGraph(g);
showGraph(g);
}
/*测试用例:
5 7
1 3 2
2 3 1
1 4 4
1 5 6
2 4 5
3 4 1
5 2 8
*/
首先我们创建了一个数据类型为图的变量g,对g进行初始化,然后创建图的边信息,打印图的邻接矩阵。主函数下面有一个测试用例,让我们检测一下代码的正确性吧!
#include
#include
#include
#define MAX 100
#define INFINITY 9999
enum graphType {DG, UG, DN, UN}; //图的类型定义:有向图,无向图,有向网,无项网
typedef char vertexType;
typedef struct {
vertexType vexs[MAX];
int arcs[MAX][MAX];
int vexnum, arcnum;
graphType kind;
} graph;
void initGraph(graph &g) {
g.kind = UN;
printf("输入顶点数和边数:\n");
scanf("%d%d", &g.vexnum, &g.arcnum);
for (int i = 1; i <= g.vexnum; i++) {
g.vexs[i] = i;
}
for (int i = 1; i <= g.vexnum; i++) {
for (int j = 1; j < g.vexnum; j++) {
if (i == j) g.arcs[i][j] = 0;
else g.arcs[i][j] = INFINITY;
}
}
}
void createGraph(graph &g) {
int start_index, end_index, weight;
printf("输入每条边的起点终点下标和边的权重:\n");
for (int i = 1; i <= g.arcnum; i++) {
scanf("%d%d%d", &start_index, &end_index, &weight);
g.arcs[start_index][end_index] = weight;
g.arcs[end_index][start_index] = weight;
}
}
void showGraph(graph &g) {
printf("邻接矩阵:\n");
for (int i = 1; i <= g.vexnum; i++) {
for (int j = 1; j <= g.vexnum; j++) {
printf("%d ", g.arcs[i][j]);
}
printf("\n");
}
}
int main() {
graph g;
initGraph(g);
createGraph(g);
showGraph(g);
}
/*测试用例:
5 7
1 3 2
2 3 1
1 4 4
1 5 6
2 4 5
3 4 1
5 2 8
*/
代码运行截图:
我们一共创建了五个定点,分别是1、2、3、4、5,一共有7条边存在,邻接矩阵第一行表示顶点1与其他顶点的边的信息,对照我们的输入信息,顶点1与顶点3边权值为2,顶点1与顶点4权值为4,顶点1与顶点5权值为6,第一行正确,依次比较剩下四行,都正确,说明我们的代码基本上没有问题。
我们今天学习了邻接矩阵(无图有权)的简单实现,你还记得代码的实现步骤吗?图结构体中包括图的顶点数量和边数量,一个一维数组存储顶点的信息,一个二维数组存储存储边的信息,一个变量存储图的类型。接下来维护图的边的信息,arcs[i][j]表示i顶点到j顶点的边权值,无向图不要忘记起点到终点,终点到起点的权值都需要维护,展示邻接矩阵就是遍历二维数组,打印每个元素的值。
最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。
同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。
谢谢您的支持,期待与您在下一篇文章中再次相遇!