第五次实验报告-图
集美大学课程实验报告-实5:图
| 项目名称 | 内容 |
|---|---|
| 课程名称 | 数据结构 |
| 班级 | 网安2411 |
| 指导教师 | 郑如滨 |
| 学生姓名 | 吴承桓 |
| 学号 | 202421336026 |
| 实验项目名称 | 图的应用 |
| 上机实践日期 | 2025/5/15 |
| 上机实践时间 | 2学时 |
一、目的(本次实验所涉及并要求掌握的知识点)
- 掌握图的基本概念和应用。
- 学习DFS与BFS的实现与优化。
- 理解图在实际问题中的应用。
二、实验内容与设计思想
题目1:图的创建
伪代码如下
// 定义最大顶点数
常量 MAX_VERTEX_NUM = 100
// 图的邻接矩阵存储表示
结构体 MGraph {
字符数组 vexs[MAX_VERTEX_NUM] // 顶点表
二维整数数组 arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM] // 邻接矩阵
整数 vexnum, arcnum // 图的当前顶点数和边数
}
// 根据顶点值查找顶点在顶点表中的位置
函数 LocateVex(G: MGraph, v: 字符) 返回 整数:
循环 i 从 0 到 G.vexnum - 1:
如果 G.vexs[i] 等于 v:
返回 i
返回 -1 // 顶点不存在
// 创建无向图
函数 CreateUDG(引用 G: MGraph):
输出 "请输入顶点数和边数: "
输入 G.vexnum 和 G.arcnum
// 初始化顶点表
输出 "请输入" + G.vexnum + "个顶点: "
循环 i 从 0 到 G.vexnum - 1:
输入 G.vexs[i]
// 初始化邻接矩阵(所有元素设为0)
将 G.arcs 的所有元素设为0
// 输入边信息
输出 "请输入" + G.arcnum + "条边(格式:顶点1 顶点2):"
循环 k 从 0 到 G.arcnum - 1:
输入 v1 和 v2
i = LocateVex(G, v1)
j = LocateVex(G, v2)
如果 i 不等于 -1 且 j 不等于 -1:
G.arcs[i][j] = 1
G.arcs[j][i] = 1 // 无向图是对称的
// 打印图的邻接矩阵
函数 PrintGraph(G: MGraph):
输出 "图的邻接矩阵表示:"
输出 " "
循环 i 从 0 到 G.vexnum - 1:
输出 G.vexs[i] + " "
输出 换行符
循环 i 从 0 到 G.vexnum - 1:
输出 G.vexs[i] + " "
循环 j 从 0 到 G.vexnum - 1:
输出 G.arcs[i][j] + " "
输出 换行符
// 主函数
主函数:
声明 MGraph 类型的变量 G
调用 CreateUDG(G)
调用 PrintGraph(G)
返回 0
函数代码
#include <iostream>
#include <cstring> // 用于memset
using namespace std;
#define MAX_VERTEX_NUM 100 // 最大顶点数
// 图的邻接矩阵存储表示
typedef struct {
char vexs[MAX_VERTEX_NUM]; // 顶点表
int arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; // 邻接矩阵
int vexnum, arcnum; // 图的当前顶点数和边数
} MGraph;
// 根据顶点值查找顶点在顶点表中的位置
int LocateVex(MGraph G, char v) {
for (int i = 0; i < G.vexnum; ++i) {
if (G.vexs[i] == v) {
return i;
}
}
return -1; // 顶点不存在
}
// 创建无向图
void CreateUDG(MGraph& G) {
cout << "请输入顶点数和边数: ";
cin >> G.vexnum >> G.arcnum;
// 初始化顶点表
cout << "请输入" << G.vexnum << "个顶点: ";
for (int i = 0; i < G.vexnum; ++i) {
cin >> G.vexs[i];
}
// 初始化邻接矩阵
memset(G.arcs, 0, sizeof(G.arcs));
// 输入边信息
cout << "请输入" << G.arcnum << "条边(格式:顶点1 顶点2):" << endl;
for (int k = 0; k < G.arcnum; ++k) {
char v1, v2;
cin >> v1 >> v2;
int i = LocateVex(G, v1);
int j = LocateVex(G, v2);
if (i != -1 && j != -1) {
G.arcs[i][j] = 1;
G.arcs[j][i] = 1; // 无向图是对称的
}
}
}
// 打印图的邻接矩阵
void PrintGraph(MGraph G) {
cout << "图的邻接矩阵表示:" << endl;
cout << " ";
for (int i = 0; i < G.vexnum; ++i) {
cout << G.vexs[i] << " ";
}
cout << endl;
for (int i = 0; i < G.vexnum; ++i) {
cout << G.vexs[i] << " ";
for (int j = 0; j < G.vexnum; ++j) {
cout << G.arcs[i][j] << " ";
}
cout << endl;
}
}
int main() {
MGraph G;
CreateUDG(G);
PrintGraph(G);
return 0;
}
//测试数据如下
/*6 7
a
b
c
d
e
f
a b
a e
b e
b f
c d
c f
d f*/
题目2:图的遍历
函数相关伪代码
伪代码
// 全局变量
bool visited[MAX_VERTEX_NUM]; // 访问标记数组
// 初始化访问标记数组
function InitVisited(G):
for i from 0 to G.vexnum - 1:
visited[i] = false
// 递归实现DFS遍历
function DFS(G, v):
print G.vexs[v] // 访问当前顶点
visited[v] = true // 标记当前顶点为已访问
// 遍历所有邻接顶点
for i from 0 to G.vexnum - 1:
if G.arcs[v][i] == 1 and not visited[i]:
DFS(G, i) // 递归访问未访问的邻接顶点
// BFS遍历
function BFS(G, v):
queue q // 创建队列
print G.vexs[v] // 访问起始顶点
visited[v] = true // 标记起始顶点为已访问
q.enqueue(v) // 起始顶点入队
while q is not empty:
current = q.dequeue() // 取出队首顶点
// 遍历所有邻接顶点
for i from 0 to G.vexnum - 1:
if G.arcs[current][i] == 1 and not visited[i]:
print G.vexs[i] // 访问顶点
visited[i] = true // 标记顶点为已访问
q.enqueue(i) // 顶点入队
函数代码
bool visited[MAX_VERTEX_NUM];
// 初始化访问标记数组
void InitVisited(MGraph G) {
for (int i = 0; i < G.vexnum; ++i) {
visited[i] = false;
}
}
// 递归实现DFS遍历
void DFS(MGraph G, int v) {
cout << G.vexs[v] << " "; // 访问顶点
visited[v] = true; // 标记为已访问
// 遍历所有邻接顶点
for (int i = 0; i < G.vexnum; ++i) {
if (G.arcs[v][i] == 1 && !visited[i]) {
DFS(G, i); // 递归访问未访问的邻接顶点
}
}
}
// BFS遍历
void BFS(MGraph G, int v) {
queue<int> q; // 创建队列
cout << G.vexs[v] << " "; // 访问起始顶点
visited[v] = true; // 标记为已访问
q.push(v); // 起始顶点入队
while (!q.empty()) {
int current = q.front(); // 取出队首顶点
q.pop();
// 遍历所有邻接顶点
for (int i = 0; i < G.vexnum; ++i) {
if (G.arcs[current][i] == 1 && !visited[i]) {
cout << G.vexs[i] << " "; // 访问顶点
visited[i] = true; // 标记为已访问
q.push(i); // 顶点入队
}
}
}
}
题目3:图着色问题
函数相关伪代码
// 输入图的基本信息
输入 V, E, K
edges = 空列表
// 输入所有边
循环 i 从 1 到 E:
输入 a, b
将 (a, b) 添加到 edges
// 输入待检查的颜色分配方案数量
输入 N
循环 i 从 1 到 N:
colors = 大小为 V+1 的数组
color_set = 空集合
// 输入颜色分配方案
循环 j 从 1 到 V:
输入 colors[j]
将 colors[j] 添加到 color_set
// 检查颜色数量是否为 K
如果 color_set 的大小 != K:
输出 "No"
继续下一次循环
valid = True
// 检查相邻顶点颜色是否相同
对于 edges 中的每一条边 (u, v):
如果 colors[u] == colors[v]:
valid = False
跳出循环
// 输出结果
如果 valid 为 True:
输出 "Yes"
否则:
输出 "No"
函数代码
#include <iostream>
#include <vector>
#include <unordered_set>
using namespace std;
int main() {
int V, E, K;
cin >> V >> E >> K;
vector<pair<int, int>> edges;
for (int i = 0; i < E; ++i) {
int a, b;
cin >> a >> b;
edges.emplace_back(a, b);
}
int N;
cin >> N;
while (N--) {
vector<int> colors(V + 1);
unordered_set<int> color_set;
for (int i = 1; i <= V; ++i) {
cin >> colors[i];
color_set.insert(colors[i]);
}
if (color_set.size() != K) {
cout << "No" << endl;
continue;
}
bool valid = true;
for (const auto& edge : edges) {
int u = edge.first;
int v = edge.second;
if (colors[u] == colors[v]) {
valid = false;
break;
}
}
cout << (valid ? "Yes" : "No") << endl;
}
return 0;
}
题目4:公路村村通(最小生成树)
函数相关伪代码
// 定义边的结构体
STRUCT Edge
u: INTEGER // 顶点u
v: INTEGER // 顶点v
cost: INTEGER // 边的成本
END STRUCT
// 并查集的查找函数,带路径压缩
FUNCTION find(u: INTEGER, parent: ARRAY OF INTEGER) RETURNS INTEGER
IF parent[u] != u THEN
parent[u] = find(parent[u], parent) // 路径压缩
END IF
RETURN parent[u]
END FUNCTION
// 并查集的合并函数
FUNCTION unite(u: INTEGER, v: INTEGER, parent: ARRAY OF INTEGER) RETURNS BOOLEAN
rootU = find(u, parent)
rootV = find(v, parent)
IF rootU == rootV THEN
RETURN FALSE // 已经在同一集合中
ELSE
parent[rootV] = rootU // 合并集合
RETURN TRUE
END IF
END FUNCTION
// 主程序
FUNCTION main()
// 输入顶点数n和边数m
READ n, m
// 初始化边的数组
edges = ARRAY OF Edge WITH SIZE m
FOR i FROM 0 TO m-1 DO
READ edges[i].u, edges[i].v, edges[i].cost
edges[i].u = edges[i].u - 1 // 转换为0-based索引
edges[i].v = edges[i].v - 1
END FOR
// 按边的成本升序排序
SORT edges BY cost
// 初始化并查集
parent = ARRAY OF INTEGER WITH SIZE n
FOR i FROM 0 TO n-1 DO
parent[i] = i
END FOR
totalCost = 0
edgesUsed = 0
// Kruskal算法
FOR EACH e IN edges DO
IF unite(e.u, e.v, parent) THEN
totalCost = totalCost + e.cost
edgesUsed = edgesUsed + 1
IF edgesUsed == n-1 THEN
BREAK // 已经找到足够的边
END IF
END IF
END FOR
// 输出结果
IF edgesUsed == n-1 THEN
PRINT totalCost
ELSE
PRINT -1 // 无法连通所有顶点
END IF
END FUNCTION
函数代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct Edge {
int u, v, cost;
bool operator<(const Edge &other) const {
return cost < other.cost;
}
};
vector<int> parent;
int find(int u) {
if (parent[u] != u) {
parent[u] = find(parent[u]);
}
return parent[u];
}
bool unite(int u, int v) {
int rootU = find(u);
int rootV = find(v);
if (rootU == rootV) return false;
parent[rootV] = rootU;
return true;
}
int main() {
int n, m;
cin >> n >> m;
vector<Edge> edges(m);
for (int i = 0; i < m; ++i) {
cin >> edges[i].u >> edges[i].v >> edges[i].cost;
edges[i].u--; // 转换为0-based
edges[i].v--;
}
sort(edges.begin(), edges.end());
parent.resize(n);
for (int i = 0; i < n; ++i) {
parent[i] = i;
}
int totalCost = 0;
int edgesUsed = 0;
for (const Edge &e : edges) {
if (unite(e.u, e.v)) {
totalCost += e.cost;
edgesUsed++;
if (edgesUsed == n - 1) break;
}
}
if (edgesUsed == n - 1) {
cout << totalCost << endl;
} else {
cout << -1 << endl;
}
return 0;
}
三、实验使用环境(本次实验所使用的平台和相关软件)
- 操作系统:Windows 11
- 编程语言:C++
- 开发工具:Visual Studio 2022
四、实验步骤和调试过程(实验步骤、测试数据设计、测试结果分析)
题目1:图的创建
本机运行截图

题目2:图的遍历
本机运行截图

题目3:图着色问题
本机运行截图

PTA提交截图

题目4:公路村村通(最小生成树)
本机运行截图

PTA提交截图

五、实验小结(实验中遇到的问题及解决过程、实验体会和收获)
遇到的问题及解决方法:
- 问题:对memset的初始化方式用法较为生疏。
- 解决方法:查阅资料,回顾实验课老师的讲解。
- 问题:对于队列在BFS算法中的应用的巧妙性。
- 解决方法:对代码进行逐行批注,反复理解。
实验体会和收获:
- 学会了如何创建图的数据结构。
- 掌握了图的两种遍历方式(BFS与DFS)。
- 体会了最小生成树在实际问题中的应用。
六、附件(参考文献和相关资料)
- 相关博客文章。
- 借助AI辅助编码。
- 老师发布的相关资料。

浙公网安备 33010602011771号