对于图的储存结构,最常用有两种方式:
- 邻接矩阵的存储
- 邻接链表的存储
今天我们就来介绍一下关于图结构中,邻接矩阵到底是什么,到底该如何存储,示例代码又是怎样等问题。
我们从最简单的说起,如果给你下面一张图,怎么样才能把这张图给存储到计算机中呢?
从图中我们可以看到,对这样一个图结构来说,最引人注目的不就是图中的顶点和边了,就是这两个元素构成了一个图,图也表示了两个或者多个顶点之间是否有关系。介绍完了图“长”这样以后,我们来讨论下图的种类;图分为有向图和无向图两种,顾名思义,就是表示图中的边是否有方向。
接下来我们说说如何用邻接矩阵把这两种图存储起来(实际上代码上只有小小的区别,只是两种理解不一样)。
首先对于无向图来说,我们首先要让计算机知道我们想要存储的图有几个顶点,有几条边,这很重要,所以我们在程序的开始就需要n(顶点)和e(边数),我们直接看下图这个例子:
对于后面这个图,仔细想想的确能够表示前面这个图,1表示存在边,0表示不存在边;看矩阵,比如(1,1),表示1顶点和1顶点没有关系(在数据结构中,我们认为顶点自己的自环不算存在关系,当然在离散数学中是可以的),再比如(1,2)表示1和2顶点有关系,比如(3,4),表示3,4两个顶点有边。比如有4个顶点,那就生成4x4的矩阵。
其实细心的同学已经发现有个特点:
那就是这个矩阵是个对称矩阵,因为这是一个无向图,1和2有边,2和1也一定有边吧,就好像两个人是好朋友一样,互相有关系。
接着我们说说有向图,其实理解了无向图,其实有向图也就很容易了,不就是多个方向嘛。
此时我们就要注意了对于有向图中,矩阵样子是和无向图差不多的,但是理解不一样;比如(1,2),在无向图中表示顶点1和顶点2有关系,而<1,2>在有向图指顶点1指向顶点2有一条边,这里要分清楚集合和序偶的概念。所以,自然有向图的矩阵就不一定对称了。
我以第一个无向图为例子,运行代码:
接下来说说关于权值的问题,什么是权值,也就是说这条边的的“权力大小”,也就是这条边的特殊性,把之前的有向图加上权值就是这样:
也就是给每个边加上权值,在以后图的遍历中,权值的大小也会决定图遍历的路径。但是对于邻接矩阵来说,无非就是把之前的“1”改为对应的权值就行了,很简单。
接下来的代码示例,指的是(有向图+权值)的例子,要改为无向图或者取消权值,稍加修改就可以,我已经注释在代码中了。
# include <stdio.h>
# include <stdlib.h>
# define MAX 20
typedef int VexType;
typedef VexType Mgraph[MAX][MAX]; //二维矩阵
void creat_mg(Mgraph G);
void out_mg(Mgraph G);
Mgraph G1;
int n,e;
main() //主函数
{ creat_mg(G1); out_mg(G1);
}
void creat_mg(Mgraph G) //创建邻接矩阵
{ int i,j,k,w;
printf("\n 输入顶点数n和边数e (空格分开)=?"); scanf("%d%d", &n,&e);
for(i=1; i<=n;i++)
for(j=1;j<=n;j++) G[i][j]=0; //这里是矩阵的初始化
for(k=1;k<=e;k++)
{ printf("\n vi,vj,权值w(空格分开)=?");
scanf("%d%d%d", &i,&j,&w); //如果不需要权值,就不用输入w
G[i][j]=w; //如果是无向图这里就为“G[i][j]=1,G[j][1]=1;”
//相当于对称矩阵,对角线复制一下就行了
}
}
void out_mg(Mgraph G) //打印邻接矩阵
{ int i,j,k; char ch;
printf("邻接矩阵为:\n");
for(i=1; i<=n;i++)
{ printf("\n ");
for(j=1;j<=n;j++) printf("%5d",G[i][j]);
}
for(i=1; i<=n;i++)
for(j=1;j<=n;j++)
if(G[i][j]!=0)printf("\n 存在边< %d,%d >",i,j);
printf("\n\n 打回车键,继续。");getchar();
}
其实对于图来说,邻接矩阵的存储是不难的,拿出笔来,自己在纸上画一画图,相信很容易就能理解的