图的存储及遍历
- 目的
- 了解图的存储方式中的邻接矩阵
- 理解深度优先遍历算法的原理和实验方法
- 要求
- 实现图的存储,采用邻接矩阵进行存储
- 实现深度优先遍历算法
- 测试并验证算法的正确性
- 原理
邻接矩阵(Adjacency Matrix)是一种表示图的方法,用矩阵来描述图中各个顶点之间的连接关系。对于一个有n个顶点的图,邻接矩阵是一个n×n的矩阵。如果图中的顶点vi和vj之间存在边,则对应的矩阵元素a[i][j]或a[j][i]为1;否则为0。对于无向图而言,邻接矩阵是对称的。邻接矩阵的优点是可以快速地判断两个顶点之间是否存在边,但当图边的数量很大时,邻接矩阵会占用较多的存储空间。
图1.图的邻接图示
图2.无向图的邻接矩阵
深度优先遍历(Depth-First Search,DFS)是一种用于遍历或搜索图或树的算法。它从图的某个顶点开始,沿着一条路径一直走到底,然后回溯到上一个顶点,继续探索其他路径,直到所有的节点都被访问过。在深度优先遍历过程中,每个顶点只能被访问一次,且保证所有的连通节点都被访问到。
- 步骤和结果
1. 结构体定义部分:
- 定义了一个结构体 Graph,用来表示图结构。其中包括存储顶点信息的数组 vertex,存储边信息的二维数组 edge,顶点数 vertex_num,边数 edge_num。
- 定义了一个全局数组 visited,用来记录顶点是否被访问过。
2. 初始化图部分:
- init_graph 函数用来初始化图,将顶点数和边数置为0,并将相关数组初始化为空或0。
3. 添加顶点和边部分:
- add_vertex 函数用来向图中添加顶点,将顶点信息存储到 vertex 数组中。
- add_edge 函数用来向图中添加边,通过修改 edge 数组来表示顶点之间的连接关系,并增加边数。
4. 深度优先遍历部分:
- dfs 函数是实现深度优先搜索的核心部分。它接受一个图结构指针和一个顶点编号作为参数,递归地访问与当前顶点相邻的未被访问过的顶点,并标记为已访问。在递归过程中输出顶点信息,实现深度优先遍历。
5. 主函数部分:
- 在主函数中,首先初始化一个图结构 g,并向其中添加顶点和边。
- 然后输出邻接矩阵,显示顶点之间的连接关系。
- 最后进行深度优先遍历,从顶点0开始进行深度优先搜索,并输出遍历结果。
图3.实验结果
- 实验中出现的问题及解决方法
在实现深度优先遍历算法时,遇到了递归调用的问题,导致程序进入死循环,经过分析发现是没有正确编辑已经访问过的结点,导致重复访问。
解决方法是在遍历过程中标记已经访问过的结点,并在递归调用前进行判断避免重复访问。
- 实验总结及体会
通过本次实验,我学会了图的邻接矩阵存储方式和深度优先遍历算法的实现方式。同时,也意识到在编程中需要严格注意边界条件和状态标记,避免程序出现死循环的问题。
- 代码部分
-
#include <stdio.h> #include <stdlib.h> #define MAX_VERTEX_NUM 6 // 顶点数的最大值 typedef struct { char vertex[MAX_VERTEX_NUM]; // 存储顶点信息 int edge[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; // 存储边信息 int vertex_num; // 顶点数 int edge_num; // 边数 } Graph; int visited[MAX_VERTEX_NUM]; // 记录顶点是否被访问过 // 初始化图 void init_graph(Graph *g) { int i, j; g->vertex_num = 0; g->edge_num = 0; for (i = 0; i < MAX_VERTEX_NUM; i++) { g->vertex[i] = '\0'; for (j = 0; j < MAX_VERTEX_NUM; j++) { g->edge[i][j] = 0; } } } // 添加顶点 void add_vertex(Graph *g, char v) { g->vertex[g->vertex_num++] = v; } // 添加边 void add_edge(Graph *g, int v1, int v2) { g->edge[v1][v2] = 1; g->edge[v2][v1] = 1; g->edge_num++; } // 深度优先遍历 void dfs(Graph *g, int v) { int i; visited[v] = 1; printf("%c ", g->vertex[v]); for (i = 0; i < g->vertex_num; i++) { if (g->edge[v][i] == 1 && visited[i] == 0) { dfs(g, i); } } } int main() { Graph g; int i, j, v1, v2; init_graph(&g); // 添加顶点 for (i = 0; i < MAX_VERTEX_NUM; i++) { add_vertex(&g, 'A' + i); } // 添加边 add_edge(&g, 0, 1); add_edge(&g, 1, 5); add_edge(&g, 1, 2); add_edge(&g, 2, 5); add_edge(&g, 5, 3); add_edge(&g, 5, 4); add_edge(&g, 3, 4); add_edge(&g, 4, 0); // 输出邻接矩阵 printf("邻接矩阵:\n"); for (i = 0; i < g.vertex_num; i++) { for (j = 0; j < g.vertex_num; j++) { printf("%d ", g.edge[i][j]); } printf("\n"); } // 深度优先遍历 printf("深度优先遍历:\n"); for (i = 0; i < g.vertex_num; i++) { visited[i] = 0; } dfs(&g, 0); printf("\n"); return 0; }
#include <stdio.h>
#include <stdlib.h>
#define MAX_VERTEX_NUM 6 // 顶点数的最大值
typedef struct {
char vertex[MAX_VERTEX_NUM]; // 存储顶点信息
int edge[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; // 存储边信息
int vertex_num; // 顶点数
int edge_num; // 边数
} Graph;
int visited[MAX_VERTEX_NUM]; // 记录顶点是否被访问过
// 初始化图
void init_graph(Graph *g) {
int i, j;
g->vertex_num = 0;
g->edge_num = 0;
for (i = 0; i < MAX_VERTEX_NUM; i++) {
g->vertex[i] = '\0';
for (j = 0; j < MAX_VERTEX_NUM; j++) {
g->edge[i][j] = 0;
}
}
}
// 添加顶点
void add_vertex(Graph *g, char v) {
g->vertex[g->vertex_num++] = v;
}
// 添加边
void add_edge(Graph *g, int v1, int v2) {
g->edge[v1][v2] = 1;
g->edge[v2][v1] = 1;
g->edge_num++;
}
// 深度优先遍历
void dfs(Graph *g, int v) {
int i;
visited[v] = 1;
printf("%c ", g->vertex[v]);
for (i = 0; i < g->vertex_num; i++) {
if (g->edge[v][i] == 1 && visited[i] == 0) {
dfs(g, i);
}
}
}
int main() {
Graph g;
int i, j, v1, v2;
init_graph(&g);
// 添加顶点
for (i = 0; i < MAX_VERTEX_NUM; i++) {
add_vertex(&g, 'A' + i);
}
// 添加边
add_edge(&g, 0, 1);
add_edge(&g, 1, 5);
add_edge(&g, 1, 2);
add_edge(&g, 2, 5);
add_edge(&g, 5, 3);
add_edge(&g, 5, 4);
add_edge(&g, 3, 4);
add_edge(&g, 4, 0);
// 输出邻接矩阵
printf("邻接矩阵:\n");
for (i = 0; i < g.vertex_num; i++) {
for (j = 0; j < g.vertex_num; j++) {
printf("%d ", g.edge[i][j]);
}
printf("\n");
}
// 深度优先遍历
printf("深度优先遍历:\n");
for (i = 0; i < g.vertex_num; i++) {
visited[i] = 0;
}
dfs(&g, 0);
printf("\n");
return 0;
}