集美大学课程实验报告-实验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

四、实验步骤和调试过程(实验步骤、测试数据设计、测试结果分析)

以下请根据实际情况编写



五、实验小结(实验中遇到的问题及解决过程、实验体会和收获)

以下请根据实际情况编写

遇到的问题及解决方法:

  1. 问题:邻接矩阵不对称
    • 解决方法:g【i】【j】=g【j】【i】使矩阵对称
  2. 问题:不知道如何dfs
    • 解决方法:使用递归

实验体会和收获:

通过学习图的邻接矩阵表示及 DFS、BFS 遍历算法,掌握了图结构的基本操作与应用场景,如连通性判断和路径搜索。


posted @ 2025-05-26 20:17  林宇钦  阅读(62)  评论(0)    收藏  举报