数据结构与算法 -- 图(邻接矩阵)原理详解
PS:图在数据结构中有着非常大的分量,它比树有着更为复杂的形式结构,这里就不再说图的基本概念,直接就说图的存储结构,邻接矩阵和邻接表。图是有方向的,有方向的叫做弧,无方向的叫做边。存储图中各顶点本身数据,使用一维数组就足够了;存储顶点之间的关系时,要记录每个顶点和其它所有顶点之间的关系,所以需要使用二维数组。图在大多行业中的使用也是很多的,比如说游戏中两个人物的寻址,自动寻路,就是图与图直接经过计算然后移动。后序还会介绍Dijkstra(迪杰斯特拉)算法计算最短路径问题。
下面介绍邻接矩阵原理:
下面可以看到顶点之间有一定的联系,如果想要把他们存放在计算机中怎么存入呢,首先我们想到的是把顶点存在一维数组中,那么他们的关系存在在二维数组中,就好像是如下格式,A->B 权值是10,B->E 权值30。(下方1为有关系,0为没有关系,未加入权值)
思路
首先把要知道顶点和边数,然后单独把顶点存在一维数组中,根据边来确定两个顶点之间的联系,比如说第一条边,是A->B。归根结底也是通过数组来存储。当然这是邻接矩阵。
步骤
- 定义结构体
- 输入顶点和边数
- 通过顶点和边数初始化数据(内部全是0或者是无穷)
- 打印表
- 遍历(深度和广度优先遍历)
1:结构体定义
1 2 3 4 5 6 7 8 9 10 | typedef char VertexType; typedef int EdgeType; #define MAXVEX 100 #define IUNFINITY 65535 typedef struct { VertexType vexs[MAXVEX]; /* 顶点表*/ EdgeType arc[MAXVEX][MAXVEX]; /* 邻接矩阵 */ int vnum, edgenum; /*定点的个数和边的个数*/ } MGraphy; |
2:数据初始化
代码中有很多注释,有没有用到的,但是一种经验。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | //加入权值 void createGraphyWeight(MGraphy *g) { printf ( "输入总顶点(空格)边数\n" ); scanf ( "%d %d" , &g->vnum, &g->edgenum); printf ( "输入 顶点表示:\n" ); //顶点请输入; for ( int i = 0; i < g->vnum; i++) { printf ( "请输入第%d个顶点" , (i + 1)); // fflush(stdin);//不起作用,资料显示一些linux平台下一些库没有定义这个方法。 // flushall(); //清除多余的回车符。 //如果不加入getchar的话,在for循环中就会先执行一遍scanf,因为上面可能会有一些回车,导致执行一遍scanf。需要清除之前的回车。 getchar (); scanf ( "%c" , &g->vexs[i]); } for ( int i = 0; i < g->vnum; i++) { // 初始化数组元素 Infonity for ( int j = 0; j < g->vnum; j++) { // g->arc[i][j] = IUNFINITY;//对于加权值的默认全部设置为最大值, g->arc[i][j] = 0; //对于未加权值的默认全部设置为0 } } printf ( "输入边有关的两个顶点,\n" ); for ( int i = 0; i < g->edgenum; i++) { char a, b; int c ; printf ( "输入第 %d 条边有关的两个顶点加权值(空格隔开),没有权值输入0\n " , (i + 1)); getchar (); // setbuf(stdin,NULL); scanf ( "%c %c %d" , &a, &b,&c); int ii = localGV(g, a); int jj = localGV(g, b); if (c == 0){ c=1; } // printf("c的值是%d",c); g->arc[ii][jj] = c; g->arc[jj][ii] = c; // 无向图 } printfL(g); } |
3:打印表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | void printfL(MGraphy *g) { //输出图的信息 printf ( "表为 :\n" ); int i = 0; //先打印行标题;顶点标题 for (i = 0; i < g->vnum + 1; i++) { if (i > 0) { printf ( "%c\t" , g->vexs[i - 1]); } else { printf ( "\\\t" ); } } printf ( "\n" ); for (i = 0; i < g->vnum; i++) { printf ( "%c\t" , g->vexs[i]); for ( int j = 0; j < g->vnum; j++) { printf ( "%d\t" , g->arc[i][j]); } printf ( "\n" ); } } |
4:深度优先遍历
这里的深度优先遍历只给出代码,原理后序会给出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | //深度优先搜索 void DFSTraverse(MGraphy *G){ // int v; //将用做标记的visit数组初始化为false for ( v = 0; v < G->vnum; ++v){ visited[v] = false ; } //对于每个标记为false的顶点调用深度优先搜索函数 for ( v = 0; v < G->vnum; v++){ //如果该顶点的标记位为false,则调用深度优先搜索函数 if (!visited[v]){ DFS(G, v); } } } int FirstAdjVex(MGraphy *g, int v) { //查找与数组下标为v的顶点之间有边的顶点,返回它在数组中的下标 for ( int i = 0; i<g->vnum; i++){ if ( g->arc[v][i]){ return i; } } return -1; } int NextAdjVex(MGraphy *G, int v, int w) { //从前一个访问位置w的下一个位置开始,查找之间有边的顶点 for ( int i = w+1; i<G->vnum; i++){ //最关键的一个判断,调试了很久,让其等于'1'或者 !=0 ,否则该字符不知道后面还是否有值相连接。 if (G->arc[v][i] != 0){ return i; } } return -1; } void DFS(MGraphy *g, int v){ visited[v]= true ; printf ( "%c" ,g->vexs[v]); //输入data值 //从该顶点的第一个边开始,一直到最后一个边,对处于边另一端的顶点调用DFS函数 int w; for ( w= FirstAdjVex(g,v); w>=0; w = NextAdjVex(g,v,w)){ //如果该顶点的标记位false,证明未被访问,调用深度优先搜索函数 if (!visited[w]){ DFS(g,w); } } } |
完
本文版权归作者所有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则保留追究法律责任的权利。
分类:
数据结构与算法
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)