图的邻接矩阵实现以及遍历
图的邻接矩阵实现以及遍历
文章目录
- 图的邻接矩阵实现以及遍历
- 一、前言
- 二、图的遍历
- 2.1 深搜(DFSDFSDFS)
- 2.1.1 概念
- 2.1.2 思想
- 2.1.3 过程
- 2.2 广搜(BFSBFSBFS)
- 2.2.1 概念
- 2.2.2 过程
- 三、邻接矩阵的实现
- 3.1 定义图结构
- 3.2 初始化
- 3.3 添边
- 3.4 访问
- 3.5 深搜
- 3.6 广搜
- 小结
一、前言
上文图的基本概述中已经对图的存储结构进行了一个深入解析,但是具体怎么实现呢?今天我就来挑战一下~当有了图,就像对树一样,我们需要对图中的元素进行访问处理,因此需要遍历这一关键的操作,那这又该怎样实现呢?且停留片刻,相信你一定能摸清里面的门道~
二、图的遍历
同样作为非线性结构,图的遍历和树的遍历思想基本一致,其遍历可分为:深搜(DFSDFSDFS),广搜(BFSBFSBFS)
唯一的区别:图的元素与元素之间是:m:nm:nm:n,会出现重复遍历已访问过的节点。而树不会出现这个问题(往上看只有一个元素)。接下来我将对其进行深入解读~
2.1 深搜(DFSDFSDFS)
2.1.1 概念
一条道走到黑。
2.1.2 思想
递归
2.1.3 过程
首先,有一个既定的原则:节点的访问由小到大(V1V1V1, V2V2V2…)。
为了防止重复遍历,需要引入visited数组,记录已经处理的节点。
如图:

从V1V1V1开始,可以到V2,V3V2,V3V2,V3,由于从小到大,因此到V2V2V2。V2V2V2可以到V1,V4,V5V1,V4,V5V1,V4,V5,V1V1V1遍历过了,于是到V4V4V4。继续遍历,直到V5V5V5,无路可走,只能回溯,每回退一次,查看是否有遗漏的没遍历的路径,直到最后一个点。
最终结果:V1−>V2−>V4−>V8−>V5−>V3−>V6−>V7V1->V2->V4->V8->V5->V3->V6->V7V1−>V2−>V4−>V8−>V5−>V3−>V6−>V7
2.2 广搜(BFSBFSBFS)
2.2.1 概念
广搜是层次遍历,就像是“水面波纹”、“病毒感染”,由里及外地探索可以触及的点(由本身的点能连接到的点)。
2.2.2 过程
但是基于遍历本身需要有先后之分,因此需要对“可触及的点”进行一个临时存储——队列。
同时在访问过程中,同样需要定义一个visited数组,记录已进入队列的(只要进过队列,就进行记录),以防止重复遍历。
每出队一个点(进行处理),就继续将其“所能接触到的点”入队。
如图:

V1V1V1入队(标记为已访问),V1V1V1出队(处理V1V1V1),V2V2V2,V3V3V3入队(标记),V2V2V2出队(处理),V4V4V4,V5V5V5入队(标记),以此不断循环。
最终结果:V1−>V2−>V3−>V4−>V5−>V6−>V7−>V8V1->V2->V3->V4->V5->V6->V7->V8V1−>V2−>V3−>V4−>V5−>V6−>V7−>V8
三、邻接矩阵的实现
3.1 定义图结构
// 定义数量——便于申请时预留空间
#define MaxNodeNum 20
// 定义一个足够大的值(表示边的权值)
#define INF 1E4
// 邻接矩阵的顶点结构
typedef struct
{int no; // 顶点编号char *show; // 显示顶点信息
} MatrixVertex;
// 表示边
typedef int MatrixEdge;
// 图的邻接矩阵表示法
typedef struct
{MatrixVertex vex[MaxNodeNum]; // 点集(一维数组)int NodeNum; // 对数量进行限制MatrixEdge edges[MaxNodeNum][MaxNodeNum]; // 边集(矩阵)int edgeNum; // 边的数量int directed; // 表示是否有向
} MGraph;
3.2 初始化
初始化点集合边集
如图:

// 初始化图
void initMGraph(MGraph *graph, char *names[], int num, int directed, int edgeValue)
{graph->directed = directed;graph->nodeNum = num; // 自己定义好的graph->edgeNum = 0; // 边需要自己建立// 初始化点集memset(graph->vex, 0, sizeof(graph->vex));// 初始化边集,当节点数量为n时,边的最大数量为n^2(每一个顶点都可以和其他任意的顶点建立边,// 包括它自己)memset(graph->edges, 0, sizeof(MatrixEdge) * MaxNodeNum * MaxNodeNum);for(int i = 0; i < num; ++i){// 初始化顶点信息graph->vex[i].no = i;graph->vex.show = names[i];// 初始化边for(int j = 0; j < num; ++j){graph->edges[i][j] = edgeValue; // 二维数组中存在的值(表示边的连接情况)}}
}
3.3 添边
- 判断边的合法性
- 填充矩阵
edgeValue
如图(最终):

// 判断边
static int isEdge(int weight)
{if(weight > 0 && weight < INF){return 1;}return 0;
}
// 添加边
void addMGraphEdge(MGraph *graph, int x, int y, int w)
{// 判断横坐标是否合法if(x < 0 || x > graph->nodeNum){return;}// 判断纵坐标是否合法if(y < 0 || y > graph->nodeNum){return;}if(!isEdge(graph->edges[x][y])){graph->edges[x][y] = w;if(graph->directed == 0){// 只有当图为无向图时,才需要存储这个信息graph->edges[y][x] = w;}graph->edgeNum++;}
}
3.4 访问
easy~
void visitMGraphNode(MatrixVertex *node)
{printf("\t%s", node->show);
}
3.5 深搜
- 访问顶点,标记顶点
- 从上述顶点出发,继续遍历可访问的顶点(按从小到大的顺序),循环上述过程
如图:

// 定义已访问的空间(全局变量,BFS也会用到)
static int MGraphVisited[MaxNodeNum];
// 深搜
void DFS_MGraph(MGraph *graph, int v)
{// 访问visitMGraphNode(graph->vex[v]);// 标记MGraphVisited[v] = 1;for(int i = 0; i < graph->nodeNum; ++i){if(isEdge(graph->edges[v][i]) && MGraphVisited[i] == 0){DFS_MGraph(graph, i); // 递归}}
}
3.6 广搜
- 定义一个临时队列
- 入队顶点,标记为已访问
- 出队顶点,访问,(从该顶点出发)入队可访问的顶点
- 循环上述过程
如图:

// 注意:当同时调用DFS和BFS时,因为两种遍历都需要用到标记访问的数组,因此在中间需要有一个清零的过程
void initVisited()
{memset(MGraphVisited, 0, sizeof(MGraphVisited));
}
// 广搜
void BFS_MGraph(MGraph *graph, int v)
{int que[MaxNodeNum]; // 定义临时队列int rear = 0, front = 0; int cur; // 定义尾巴,头,要访问的当前顶点rear = (rear + 1) % MaxNodeNum; // 实现尾指针的循环移动,队尾先加que[rear] = v; // 入队MGraphVisited[v] = 1; // 入队之后就进行标记while (front != rear){front = (front + 1) % MaxNodeNum; // 出队cur = que[front]; // 取出来visitMGraphNode(&graph->vex[cur]); // 访问for (int i = 0; i < graph->nodeNum; ++i) // 继续入队可遍历的节点{if (isEdge(graph->edges[cur][i]) && !MGraphVisited[i]){rear = (rear + 1) % MaxNodeNum;que[rear] = i;MGraphVisited[i] = 1;}}}
}
小结
本篇主要以邻接矩阵的实现为主,下篇将会迎来邻接表哦~期待inginging
希望各位不吝赐教~
