一、实验目的
(本次实验所涉及并要求掌握的知识点)
1.领会图的两种存储结构和图的基本运算算法设计;领会图的两种遍历算法。
2.掌握图的深度优先遍历和广度优先遍历算法在求解图路径搜索问题中的应用。
二、实验内容与设计思想
(设计思路、主要数据结构、主要代码结构、主要代码段分析)
1. 实验内容
2. 抽象数据类型定义
ADT Graph
{
数据对象:
D = {ai | 1 <= i <= n, n >= 0, ai 为ElemType类型}
数据关系:
R = {< ai,aj> | ai,aj∈D,1 <= i,j <= n,其中每个元素可以有零个或多个前驱元素,可以有零个或多个后继元素}
基本运算:
void initGraph(Graph* graph); // 初始化图
void addVertex(Graph* graph, int data); // 添加顶点
void addEdge(Graph* graph, int start, int end); // 添加边
void createAdjMatrix(Graph* graph, int adjMatrix[][MAX_VERTEX]); // 构造邻接矩阵
void printAdjMatrix(Graph* graph, int adjMatrix[][MAX_VERTEX]); // 打印邻接矩阵
void createAdjList(Graph* graph, EdgeNode* adjList[]); // 构造邻接表
void printAdjList(Graph* graph, EdgeNode* adjList[]); // 打印邻接表
void DFSRecursive(Graph* graph, int vertex, int visited[]); // 深度优先遍历递归函数
void DFS(Graph* graph); // 深度优先遍历
void BFS(Graph* graph); // 广度优先遍历
void findPaths(Graph* graph, EdgeNode* adjList[], int m, int n, int x, int path[], int visited[], int depth);// 查找从顶点m到顶点n的所有长度为x的简单路径
void findAllPaths(Graph* graph, EdgeNode* adjList[], int m, int n, int x); // 从顶点m到顶点n的所有长度为x的简单路径
bool findShortestPath(Graph* graph, int src, int dest, int path[], int* pathLength); // 使用广度优先搜索找到最短路径
void printShortestPath(Graph* graph, int src, int dest); // 打印最短路径
void destroyAdjList(Graph* graph, EdgeNode* adjList[]); // 销毁邻接表
}
3. 设计思路
- 定义了一个结构体 Graph 作为图的数据结构,其中包含了顶点数量、边数量以及顶点数组。每个顶点包含数据和指向第一条边的指针。
- initGraph() 函数用于初始化图,将顶点数量和边数量都设置为0,并将顶点数组中的数据和边指针初始化为NULL。
- addVertex() 函数用于向图中添加顶点,将指定的数据存储到顶点数组中,并更新顶点数量。
- addEdge() 函数用于向图中添加边,通过给定的起点和终点索引,创建一个新的边节点,并将其插入到起点顶点的边链表的开头,同时更新边数量。
- createAdjMatrix() 函数用于构造邻接矩阵,通过遍历顶点数组和每个顶点的边链表,将邻接矩阵中对应位置的值设置为1表示存在边。
- printAdjMatrix() 函数用于打印邻接矩阵。
- createAdjList() 函数用于构造邻接表,通过遍历顶点数组和每个顶点的边链表,将边节点逐个插入到邻接表中对应顶点的链表末尾。
- printAdjList() 函数用于打印邻接表。
- DFSRecursive() 函数为深度优先遍历的递归实现,通过递归地遍历与当前顶点相邻的未访问顶点,实现深度优先遍历。
- DFS() 函数用于进行深度优先遍历,通过遍历顶点数组,对未访问过的顶点调用 DFSRecursive() 进行深度优先遍历。
- BFS() 函数用于进行广度优先遍历,通过遍历顶点数组,对未访问过的顶点进行广度优先搜索。
- findPaths() 函数用于查找从指定起点到指定终点的所有长度为x的简单路径,通过递归遍历邻接表中的边节点,并记录已访问的路径和深度,当达到终点且深度等于x时,输出路径。
- findAllPaths() 函数用于找出从指定起点到指定终点的所有长度为x的简单路径,通过调用 findPaths() 进行查找。
- findShortestPath() 函数用于查找从指定起点到指定终点的最短路径,使用广度优先搜索算法,并记录前驱节点,最后构建出最短路径。
- printShortestPath() 函数用于打印最短路径。
- destroyAdjList() 函数用于销毁邻接表,释放内存。
4. 数据结构定义
#define MAX_VERTEX 20 // 最大顶点数
// 边结点
typedef struct EdgeNode {
int index; // 目标顶点的索引
struct EdgeNode* next; // 下一个边结点指针
} EdgeNode;
// 顶点结点
typedef struct VertexNode {
int data; // 顶点数据
EdgeNode* firstEdge; // 第一个边结点指针
} VertexNode;
// 图
typedef struct Graph {
VertexNode vertices[MAX_VERTEX]; // 顶点数组
int vertexCount; // 顶点数
int edgeCount; // 边数
} Graph;
void initGraph(Graph* graph); // 初始化图
void addVertex(Graph* graph, int data); // 添加顶点
void addEdge(Graph* graph, int start, int end); // 添加边
void createAdjMatrix(Graph* graph, int adjMatrix[][MAX_VERTEX]); // 构造邻接矩阵
void printAdjMatrix(Graph* graph, int adjMatrix[][MAX_VERTEX]); // 打印邻接矩阵
void createAdjList(Graph* graph, EdgeNode* adjList[]); // 构造邻接表
void printAdjList(Graph* graph, EdgeNode* adjList[]); // 打印邻接表
void DFSRecursive(Graph* graph, int vertex, int visited[]); // 深度优先遍历递归函数
void DFS(Graph* graph); // 深度优先遍历
void BFS(Graph* graph); // 广度优先遍历
void findPaths(Graph* graph, EdgeNode* adjList[], int m, int n, int x, int path[], int visited[], int depth);// 查找从顶点m到顶点n的所有长度为x的简单路径
void findAllPaths(Graph* graph, EdgeNode* adjList[], int m, int n, int x); // 从顶点m到顶点n的所有长度为x的简单路径
bool findShortestPath(Graph* graph, int src, int dest, int path[], int* pathLength); // 使用广度优先搜索找到最短路径
void printShortestPath(Graph* graph, int src, int dest); // 打印最短路径
void destroyAdjList(Graph* graph, EdgeNode* adjList[]); // 销毁邻接表
5. 程序中各函数调用关系及主要功能函数流程图
- createAdjMatrix() 函数内部调用了 graph->vertices[i].firstEdge 的链表遍历操作,用于构造邻接矩阵。
- printAdjMatrix() 函数内部没有直接的函数调用关系,它主要用于打印邻接矩阵。
- createAdjList() 函数内部调用了 graph->vertices[i].firstEdge 的链表遍历操作,用于构造邻接表。
- printAdjList() 函数内部没有直接的函数调用关系,它主要用于打印邻接表。
- DFS() 函数内部调用了 DFSRecursive() 函数进行深度优先遍历。
- BFS() 函数内部使用了队列结构,并调用了 graph->vertices[vertex].firstEdge 的链表遍历操作,用于进行广度优先遍历。
- findPaths() 函数内部调用了自身递归地查找所有长度为x的简单路径。
- findShortestPath() 函数内部使用了队列结构,并调用了 graph->vertices[current].firstEdge 的链表遍历操作,用于查找最短路径。
- printShortestPath() 函数内部没有直接的函数调用关系,它主要用于打印最短路径。
- destroyAdjList() 函数内部调用了 free() 函数,用于释放动态分配的内存。
三、实验使用环境(本次实验所使用的平台和相关软件)
Visual stdio