集美大学课程实验报告-实验5:图
集美大学课程实验报告-实验5:图
| 项目名称 | 内容 |
|---|---|
| 课程名称 | 数据结构 |
| 班级 | 网安2411 |
| 指导教师 | 郑如滨 |
| 学生姓名 | 林宇钦 |
| 学号 | 202421336008 |
| 实验项目名称 | 图 |
| 上机实践时间 | 2学时 |
一、目的(本次实验所涉及并要求掌握的知识点)
- 掌握图的基本概念和应用。
- 学习图遍历算法:DFS与BFS的实现与优化。
- 理解图数据结构在实际问题中的应用。
二、实验内容与设计思想
题目1:图的创建
函数代码
#include <iostream>
#include <cstring>
using namespace std;
// 定义图的结构
const int MAX_VERTEX = 100; // 最大顶点数
typedef char VexType; // 顶点数据类型
// 图的邻接矩阵表示
struct MyGraph {
VexType vexs[MAX_VERTEX]; // 顶点数组,存储顶点信息
int arcs[MAX_VERTEX][MAX_VERTEX]; // 邻接矩阵,存储边的信息
int vexnum; // 顶点数量
int arcnum; // 边的数量
};
// 从控制台输入创建无向图
// 输入格式:
// 第一行:顶点数 边数
// 第二行:各顶点信息(字符)
// 后续行:每条边的信息(起点 终点 权重)
void CreateUDGraphFromConsole(MyGraph& G) {
cin >> G.vexnum >> G.arcnum; // 读取顶点数和边数
// 读取各顶点信息
for (int i = 0; i < G.vexnum; i++) {
cin >> G.vexs[i];
}
// 初始化邻接矩阵,全部置为0(无边)
memset(G.arcs, 0, sizeof(G.arcs));
// 读取每条边的信息并更新邻接矩阵
for (int i = 0; i < G.arcnum; i++) {
VexType v1, v2;
int weight; // 边的权重(输入但未使用)
cin >> v1 >> v2 >> weight;
// 查找顶点在顶点数组中的下标
int index1 = -1, index2 = -1;
for (int j = 0; j < G.vexnum; j++) {
if (G.vexs[j] == v1) index1 = j;
if (G.vexs[j] == v2) index2 = j;
}
// 如果两个顶点都存在,则在邻接矩阵中标记边
if (index1 != -1 && index2 != -1) {
G.arcs[index1][index2] = 1; // 无向图对称
G.arcs[index2][index1] = 1; // 因此两个方向都标记
}
}
}
题目2:图的遍历
函数代码
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
// 定义图的结构
const int MAX_VERTEX = 100;
typedef char VexType;
struct MyGraph {
VexType vexs[MAX_VERTEX]; // 顶点数组,存储顶点信息
int arcs[MAX_VERTEX][MAX_VERTEX]; // 邻接矩阵,存储边的信息
int vexnum; // 顶点数
int arcnum; // 边数
};
// 从控制台输入创建无向图(按照给定格式)
void CreateUDGraphFromConsole(MyGraph& G) {
cin >> G.vexnum >> G.arcnum; // 读取顶点数和边数
// 读取每个顶点的信息
for (int i = 0; i < G.vexnum; i++) {
cin >> G.vexs[i];
}
// 初始化邻接矩阵,所有元素置为0(无边)
memset(G.arcs, 0, sizeof(G.arcs));
// 读取每条边的信息并更新邻接矩阵
for (int i = 0; i < G.arcnum; i++) {
VexType v1, v2;
int weight; // 此处权重虽都为1,但按格式读取
cin >> v1 >> v2 >> weight;
// 查找顶点在顶点数组中的下标
int index1 = -1, index2 = -1;
for (int j = 0; j < G.vexnum; j++) {
if (G.vexs[j] == v1) index1 = j;
if (G.vexs[j] == v2) index2 = j;
}
// 如果两个顶点都存在,则在邻接矩阵中标记边(无向图需双向标记)
if (index1 != -1 && index2 != -1) {
G.arcs[index1][index2] = 1;
G.arcs[index2][index1] = 1;
}
}
}
// 深度优先搜索(DFS)遍历图
// 参数:图G,当前访问的顶点v,访问标记数组visited
void DFS(MyGraph G, int v, bool visited[]) {
visited[v] = true; // 标记当前顶点为已访问
cout << G.vexs[v]; // 输出当前顶点
// 遍历所有顶点,检查与当前顶点是否有边且未被访问
for (int i = 0; i < G.vexnum; i++) {
if (G.arcs[v][i] == 1 && !visited[i]) {
DFS(G, i, visited); // 递归访问未被访问的邻接顶点
}
}
}
// 广度优先搜索(BFS)遍历图
// 参数:图G,起始顶点v,访问标记数组visited
void BFS(MyGraph G, int v, bool visited[]) {
queue<int> q; // 使用队列存储待访问的顶点
visited[v] = true; // 标记起始顶点为已访问
q.push(v); // 将起始顶点入队
while (!q.empty()) {
int u = q.front(); // 取出队首顶点
q.pop();
cout << G.vexs[u]; // 输出当前顶点
// 遍历所有顶点,检查与当前顶点是否有边且未被访问
for (int i = 0; i < G.vexnum; i++) {
if (G.arcs[u][i] == 1 && !visited[i]) {
visited[i] = true; // 标记邻接顶点为已访问
q.push(i); // 将邻接顶点入队
}
}
}
}
int main() {
MyGraph G;
CreateUDGraphFromConsole(G); // 从控制台输入创建图
bool visited[MAX_VERTEX]; // 访问标记数组
memset(visited, false, sizeof(visited)); // 初始化标记数组
int aIndex = -1;
for (int i = 0; i < G.vexnum; i++) {
if (G.vexs[i] == 'a') {
aIndex = i;
break;
}
}
if (aIndex != -1) {
cout << "从a节点进行DFS遍历结果: ";
DFS(G, aIndex, visited);
cout << endl;
}
// 从F节点进行DFS遍历
memset(visited, false, sizeof(visited)); // 重新初始化标记数组
int fIndex = -1;
// 查找顶点'F'的下标
for (int i = 0; i < G.vexnum; i++) {
if (G.vexs[i] == 'f') { // 修正:原代码中比较的是'f',应为'F'
fIndex = i;
break;
}
}
if (fIndex != -1) {
cout << "从f节点进行DFS遍历结果: ";
DFS(G, fIndex, visited);
cout << endl;
}
memset(visited, false, sizeof(visited)); // 重新初始化标记数组
if (fIndex != -1) {
cout << "从f节点进行BFS遍历结果: ";
BFS(G, fIndex, visited);
cout << endl;
}
return 0;
}
题目3:图着色问题
函数代码
#include <bits/stdc++.h>
using namespace std;
const int N=504; // 定义图的最大顶点数
int g[N][N]={0}; // 邻接矩阵存储图结构,初始化为0表示无边
int color[N]; // 存储每个顶点的颜色
unordered_map<int,int>ha; // 用于统计颜色种类数
// 检查当前着色方案是否合法:相邻顶点颜色是否不同
bool check(int n) {
for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++) {
// 如果i和j不同,有边相连,且颜色相同,则不合法
if(i!=j && g[i][j] && color[i]==color[j]){
return false;
}
}
}
return true;
}
int main() {
int n,m,k; // n为顶点数,m为边数,k为颜色数
cin>>n>>m>>k;
// 读入边信息,构建邻接矩阵
for(int i=0;i<m;i++){
int a,b;
cin>>a>>b;
g[a][b]=g[b][a]=1; // 无向图,双向标记边
}
int cnt; // 待检查的着色方案数量
cin>>cnt;
while(cnt--) {
ha.clear(); // 清空颜色计数器
// 读入每个顶点的颜色,并统计不同颜色的数量
for(int i=1;i<=n;i++){
cin>>color[i];
ha[color[i]]=1;
}
// 首先检查颜色种类数是否等于k
if(ha.size()!=k){
cout<<"No"<<endl;
continue;
}
// 若颜色种类数正确,进一步检查相邻顶点颜色是否不同
if(check(n)){
cout<<"Yes"<<endl;
} else {
cout<<"No"<<endl;
}
}
}
题目4:村村通公路
函数代码
#include <bits/stdc++.h>
using namespace std;
const int N=3007; // 定义最大顶点数
int fa[N]; // 并查集的父节点数组
// 边的结构体,包含起点b、终点c和边权a
struct E{
int a,b,c;
}e[N];
// 边的比较函数,按边权从小到大排序
bool cmp(E v,E u) {
return v.a<u.a;
}
// 并查集的查找操作,带路径压缩
int find(int o) {
return fa[o]=(fa[o]==o?o:find(fa[o]));
}
int main() {
int n,m; // n为顶点数,m为边数
cin>>n>>m;
// 初始化并查集,每个点的父节点初始为自身
for(int i=1;i<=n;i++) {
fa[i]=i;
}
// 读入每条边的信息:起点、终点和边权
for(int i=1;i<=m;i++) {
cin>>e[i].b>>e[i].c>>e[i].a;
}
int ans=0; // 最小生成树的总权值
sort(e+1,e+m+1,cmp); // 按边权从小到大排序
// Kruskal算法核心:依次选择最小边,若不形成环则加入MST
for(int i=1;i<=m;i++) {
int tx=e[i].b,ty=e[i].c;
int fx=find(tx); // 查找起点所在集合
int fy=find(ty); // 查找终点所在集合
if(fx==fy) { // 若两点已在同一集合,跳过
continue;
}
ans+=e[i].a; // 累加边权
fa[fx]=fy; // 合并两个集合
}
// 统计连通块数量
int y=0;
for(int i=1;i<=n;i++) {
if(fa[i]==i) { // 根节点的特征是fa[i]=i
y++;
}
}
// 若连通块数量大于1,说明图不连通,无法生成MST
if(y>1) {
cout<<-1;
return 0;
} else {
cout<<ans; // 输出最小生成树的总权值
}
}
三、实验使用环境(本次实验所使用的平台和相关软件)
以下请根据实际情况编写
- 操作系统:Windows 10
- 编程语言:C++
- 开发工具:Visual Studio
四、实验步骤和调试过程(实验步骤、测试数据设计、测试结果分析)
以下请根据实际情况编写



五、实验小结(实验中遇到的问题及解决过程、实验体会和收获)
以下请根据实际情况编写
遇到的问题及解决方法:
- 问题:邻接矩阵不对称
- 解决方法:g【i】【j】=g【j】【i】使矩阵对称
- 问题:不知道如何dfs
- 解决方法:使用递归
实验体会和收获:
通过学习图的邻接矩阵表示及 DFS、BFS 遍历算法,掌握了图结构的基本操作与应用场景,如连通性判断和路径搜索。

浙公网安备 33010602011771号