第五次实验报告-图

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

三、实验使用环境(本次实验所使用的平台和相关软件)


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

题目1:图的创建

本机运行截图

题目2:图的遍历

本机运行截图

题目3:图着色问题

本机运行截图

PTA提交截图

题目4:公路村村通(最小生成树)

本机运行截图

PTA提交截图


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

遇到的问题及解决方法:

  1. 问题:对memset的初始化方式用法较为生疏。
    • 解决方法:查阅资料,回顾实验课老师的讲解。
  2. 问题:对于队列在BFS算法中的应用的巧妙性。
    • 解决方法:对代码进行逐行批注,反复理解。

实验体会和收获:

  • 学会了如何创建图的数据结构。
  • 掌握了图的两种遍历方式(BFS与DFS)。
  • 体会了最小生成树在实际问题中的应用。

六、附件(参考文献和相关资料)

  1. 相关博客文章。
  2. 借助AI辅助编码。
  3. 老师发布的相关资料。
posted @ 2025-05-15 22:45  吴承桓  阅读(57)  评论(0)    收藏  举报