集美大学课程实验报告-实验5:图

项目名称 内容
课程名称 数据结构
实验项目名称 实验5:图
上机实践日期 2026.5.29
上机实践时间 2学时

一、目的(本次实验所涉及并要求掌握的知识点)

  • 学会创建图(邻接矩阵),掌握在图上的基本操作。
  • 掌握图遍历算法:DFS与BFS。
  • 掌握编写最小生成树算法。

二、实验内容与设计思想

以下请根据实际情况编写

题目1:图的创建

** LocateVex函数相关伪代码**

函数 LocateVex(G, key)
    // 输入:图G,要查找的顶点值key
    // 输出:顶点在数组中的下标,未找到返回-1
    
    for i = 0 to G.vexNum - 1 do
        if G.vexs[i] == key then
            return i
        end if
    end for
    return -1

** CreateUDGraphFromConsole函数相关伪代码**

函数 CreateUDGraphFromConsole(G)
    // 输入:图G(引用传递)
    // 输出:填充好的图G
    
    // 步骤1:输入顶点数和边数
    输入 G.vexNum, G.arcNum
    
    // 步骤2:输入顶点信息
    for i = 0 to G.vexNum - 1 do
        输入 G.vexs[i]
    end for
    
    // 步骤3:初始化邻接矩阵
    for i = 0 to G.vexNum - 1 do
        for j = 0 to G.vexNum - 1 do
            G.arcs[i][j] = 0
        end for
    end for
    
    // 步骤4:输入边信息
    for k = 0 to G.arcNum - 1 do
        输入 v1, v2, w
        i = LocateVex(G, v1)
        j = LocateVex(G, v2)
        
        if i != -1 and j != -1 then
            G.arcs[i][j] = w
            G.arcs[j][i] = w    // 无向图对称赋值
        end if
    end for

** PrintMGraph函数相关伪代码**

函数 PrintMGraph(G)
    // 输入:图G
    // 输出:打印邻接矩阵
    
    for i = 0 to G.vexNum - 1 do
    end for
    
    for i = 0 to G.vexNum - 1 do
    end for
    
    // 打印矩阵内容
    for i = 0 to G.vexNum - 1 do
        for j = 0 to G.vexNum - 1 do
        end for
    end for

函数代码

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

#define MAXVEXNUM 100

typedef int ArcCell;
typedef char VexType;

typedef struct {
    VexType vexs[MAXVEXNUM];
    ArcCell arcs[MAXVEXNUM][MAXVEXNUM];
    int vexNum;
    int arcNum;
} MyGraph;

int LocateVex(MyGraph G, VexType key) {
    for (int i = 0; i < G.vexNum; i++) {
        if (G.vexs[i] == key) {
            return i;
        }
    }
    return -1;
}

void CreateUDGraphFromConsole(MyGraph* G) {
    printf("请输入顶点数和边数:");
    scanf("%d %d", &(G->vexNum), &(G->arcNum));

    printf("请输入 %d 个顶点:", G->vexNum);
    for (int i = 0; i < G->vexNum; i++) {
        scanf(" %c", &(G->vexs[i]));
    }

    memset(G->arcs, 0, sizeof(G->arcs));

    printf("请输入 %d 条边(格式:顶点1 顶点2 权值):\n", G->arcNum);
    for (int k = 0; k < G->arcNum; k++) {
        char v1, v2;
        int w;
        scanf(" %c %c %d", &v1, &v2, &w);

        int i = LocateVex(*G, v1);
        int j = LocateVex(*G, v2);

        if (i != -1 && j != -1) {
            G->arcs[i][j] = w;
            G->arcs[j][i] = w; 
        }
    }
}

void PrintMGraph(MyGraph G) {
    printf("\n========================================\n");
    printf("邻接矩阵:\n");
    printf("    ");
    for (int i = 0; i < G.vexNum; i++) {
        printf(" %c ", G.vexs[i]);
    }
    printf("\n");
    printf("    ");
    for (int i = 0; i < G.vexNum; i++) {
        printf("---");
    }
    printf("\n");

    for (int i = 0; i < G.vexNum; i++) {
        printf(" %c | ", G.vexs[i]);
        for (int j = 0; j < G.vexNum; j++) {
            printf(" %d ", G.arcs[i][j]);
        }
        printf("\n");
    }
    printf("========================================\n");
}

int main() {
    printf("========================================\n");

    MyGraph G;

    CreateUDGraphFromConsole(&G);

    PrintMGraph(G);

    printf("\n【验证对称性】\n");
    int symmetric = 1;
    for (int i = 0; i < G.vexNum; i++) {
        for (int j = 0; j < G.vexNum; j++) {
            if (G.arcs[i][j] != G.arcs[j][i]) {
                symmetric = 0;
                break;
            }
        }
    }
    if (symmetric) {
        printf("邻接矩阵是对称的,符合无向图特性!\n");
    }
    else {
        printf("邻接矩阵不对称!\n");
    }

    printf("\n按任意键退出...");
    getchar();
    getchar();

    return 0;
}

时间复杂度

O(n² + e)

空间复杂度

O(n²)

题目2:图的遍历

DFS函数相关伪代码

函数 DFS(G, v)
    visited[v] = 1
    输出 G.vexs[v]
    
    for i = 0 to G.vexNum-1 do
        if G.arcs[v][i] == 1 and visited[i] == 0 then
            DFS(G, i)
        end if
    end for

BFS函数相关伪代码

函数 BFS(G, v)
    创建队列Q
    visited[v] = 1
    输出 G.vexs[v]
    v入队
    
    while 队列非空 do
        u = 出队
        for i = 0 to G.vexNum-1 do
            if G.arcs[u][i] == 1 and visited[i] == 0 then
                visited[i] = 1
                输出 G.vexs[i]
                i入队
            end if
        end for
    end while

LocateVex函数相关伪代码

函数 LocateVex(G, key)
    for i = 0 to G.vexNum-1 do
        if G.vexs[i] == key then return i
    end for
    return -1

函数代码

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

#define MAXVEXNUM 100
#define MAXQSIZE 100

typedef int ArcCell;
typedef char VexType;

typedef struct {
    VexType vexs[MAXVEXNUM];
    ArcCell arcs[MAXVEXNUM][MAXVEXNUM];
    int vexNum;
    int arcNum;
} MyGraph;

int visited[MAXVEXNUM];

int LocateVex(MyGraph G, VexType key) {
    for (int i = 0; i < G.vexNum; i++) {
        if (G.vexs[i] == key) {
            return i;
        }
    }
    return -1;
}

void CreateUDGraphFromConsole(MyGraph* G) {
    printf("请输入顶点数和边数:");
    scanf("%d %d", &(G->vexNum), &(G->arcNum));

    printf("请输入 %d 个顶点:", G->vexNum);
    for (int i = 0; i < G->vexNum; i++) {
        scanf(" %c", &(G->vexs[i]));
    }

    memset(G->arcs, 0, sizeof(G->arcs));

    printf("请输入 %d 条边(格式:顶点1 顶点2 权值):\n", G->arcNum);
    for (int k = 0; k < G->arcNum; k++) {
        char v1, v2;
        int w;
        scanf(" %c %c %d", &v1, &v2, &w);

        int i = LocateVex(*G, v1);
        int j = LocateVex(*G, v2);

        if (i != -1 && j != -1) {
            G->arcs[i][j] = w;
            G->arcs[j][i] = w;
        }
    }
}

void PrintMGraph(MyGraph G) {
    printf("\n========================================\n");
    printf("邻接矩阵:\n");
    printf("    ");
    for (int i = 0; i < G.vexNum; i++) {
        printf(" %c ", G.vexs[i]);
    }
    printf("\n");
    printf("    ");
    for (int i = 0; i < G.vexNum; i++) {
        printf("---");
    }
    printf("\n");

    for (int i = 0; i < G.vexNum; i++) {
        printf(" %c | ", G.vexs[i]);
        for (int j = 0; j < G.vexNum; j++) {
            printf(" %d ", G.arcs[i][j]);
        }
        printf("\n");
    }
    printf("========================================\n");
}

void DFS(MyGraph G, int v) {
    visited[v] = 1;
    printf("%c ", G.vexs[v]);

    for (int i = 0; i < G.vexNum; i++) {
        if (G.arcs[v][i] == 1 && visited[i] == 0) {
            DFS(G, i);
        }
    }
}

void DFSFromVex(MyGraph G, VexType start) {
    memset(visited, 0, sizeof(visited));
    int startPos = LocateVex(G, start);
    if (startPos != -1) {
        DFS(G, startPos);
    }
}

void BFS(MyGraph G, int v) {
    int queue[MAXQSIZE];
    int front = 0, rear = 0;

    visited[v] = 1;
    printf("%c ", G.vexs[v]);
    queue[rear++] = v;

    while (front < rear) {
        int u = queue[front++];
        for (int i = 0; i < G.vexNum; i++) {
            if (G.arcs[u][i] == 1 && visited[i] == 0) {
                visited[i] = 1;
                printf("%c ", G.vexs[i]);
                queue[rear++] = i;
            }
        }
    }
}

void BFSFromVex(MyGraph G, VexType start) {
    memset(visited, 0, sizeof(visited));
    int startPos = LocateVex(G, start);
    if (startPos != -1) {
        BFS(G, startPos);
    }
}

int main() {
    printf("========================================\n");

    MyGraph G;

    CreateUDGraphFromConsole(&G);

    PrintMGraph(G);

    printf("\n【从A节点DFS遍历】\n");
    printf("遍历结果:");
    DFSFromVex(G, 'A');
    printf("\n");

    printf("\n【从F节点DFS遍历】\n");
    printf("遍历结果:");
    DFSFromVex(G, 'F');
    printf("\n");

    printf("\n【从F节点BFS遍历】\n");
    printf("遍历结果:");
    BFSFromVex(G, 'F');
    printf("\n");

    printf("\n按任意键退出...");
    getchar();
    getchar();

    return 0;
}

时间复杂度

O(n²)

空间复杂度

O(n)

题目3:图的着色

AddEdge函数相关伪代码

函数 AddEdge(G, u, v)
    将v加入u的邻接表
    将u加入v的邻接表

CheckOne函数相关伪代码

函数 CheckOne(V, K)
    // 输入颜色
    for i = 1 to V do
        输入 color[i]
    
    // 检查颜色种数
    colorCount = 0
    memset(used, 0, sizeof(used))
    for i = 1 to V do
        if used[color[i]] == 0 then
            used[color[i]] = 1
            colorCount++
    
    if colorCount != K then
        return false
    
    // 检查相邻顶点
    for i = 1 to V do
        for 每个邻接点 j in 邻接表[i] do
            if color[i] == color[j] then
                return false
    
    return true

main函数相关伪代码

函数 main()
    输入 V, E, K
    建图
    
    输入 N
    for i = 1 to N do
        if CheckOne(V, K) then
            输出 "Yes"
        else
            输出 "No"

函数代码

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXV 505

typedef struct EdgeNode {
    int adjvex;
    struct EdgeNode* next;
} EdgeNode;

typedef struct {
    EdgeNode* adjList[MAXV];
    int vexNum;
} Graph;

void AddEdge(Graph* G, int u, int v) {
    EdgeNode* e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = v;
    e->next = G->adjList[u];
    G->adjList[u] = e;

    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = u;
    e->next = G->adjList[v];
    G->adjList[v] = e;
}

int main() {
    printf("========================================\n\n");

    Graph G;
    int V, E, K;
    scanf("%d %d %d", &V, &E, &K);

    G.vexNum = V;
    for (int i = 1; i <= V; i++) {
        G.adjList[i] = NULL;
    }

    for (int i = 0; i < E; i++) {
        int u, v;
        scanf("%d %d", &u, &v);
        AddEdge(&G, u, v);
    }

    int N;
    scanf("%d", &N);

    int color[MAXV];
    int used[MAXV];

    for (int s = 0; s < N; s++) {
        for (int i = 1; i <= V; i++) {
            scanf("%d", &color[i]);
        }

        memset(used, 0, sizeof(used));
        int colorCount = 0;
        for (int i = 1; i <= V; i++) {
            if (used[color[i]] == 0) {
                used[color[i]] = 1;
                colorCount++;
            }
        }

        if (colorCount != K) {
            printf("No\n");
            continue;
        }

        int valid = 1;
        for (int i = 1; i <= V && valid; i++) {
            EdgeNode* p = G.adjList[i];
            while (p) {
                if (color[i] == color[p->adjvex]) {
                    valid = 0;
                    break;
                }
                p = p->next;
            }
        }

        printf("%s\n", valid ? "Yes" : "No");
    }

    return 0;
}

时间复杂度

O(N × (n + e))
n = 顶点数,e = 边数,N = 着色方案数

空间复杂度

O(n + e)
n = 顶点数,e = 边数,

题目4:公路村村通

函数相关伪代码

#include <limits.h>

#define MAX_VERTICES 100

typedef struct {
    int edges[MAX_VERTICES][MAX_VERTICES];
    int n;  // 顶点数(城市数)
    int m;  // 边数(路数)
} Graph;

int Prim(Graph g) {
    int dist[MAX_VERTICES];     // 到当前树的最小距离
    int visited[MAX_VERTICES];  // 是否已加入树
    int totalCost = 0;
    
    // 1. 初始化
    for (int i = 0; i < g.n; i++) {
        dist[i] = INT_MAX;      // 无穷大
        visited[i] = 0;
    }
    
    // 2. 从第一个顶点(下标0)开始
    dist[0] = 0;
    
    // 3. 主循环:每次加入一个顶点,共 n 次
    for (int count = 0; count < g.n; count++) {
        // 3.1 找未加入且 dist 最小的顶点 u
        int u = -1;
        for (int i = 0; i < g.n; i++) {
            if (!visited[i] && (u == -1 || dist[i] < dist[u])) {
                u = i;
            }
        }
        
        // 3.2 判断不连通
        if (dist[u] == INT_MAX) {
            return -1;   // 剩下的顶点都不可达,图不连通
        }
        
        // 3.3 加入 u
        visited[u] = 1;
        totalCost += dist[u];
        
        // 3.4 更新其他顶点的距离
        for (int v = 0; v < g.n; v++) {
            // 如果 v 未加入,且 u-v 有边,且这条边比当前 dist[v] 更小
            if (!visited[v] && g.edges[u][v] < dist[v]) {
                dist[v] = g.edges[u][v];
            }
        }
    }
    
    return totalCost;
}

与ai互动过程

请点击此链接观看与deep seek的互动过程
https://chat.deepseek.com/share/uy7luol5yyw0lkoakm

函数代码

#define _CRT_SECURE_NO_WARNINGS 
#include <stdio.h>
#include <limits.h>

#define MAX_VERTICES 100

typedef struct {
    int edges[MAX_VERTICES][MAX_VERTICES];
    int n;
    int m;
} Graph;

void initGraph(Graph* g, int n, int m) {
    int i, j;
    g->n = n;
    g->m = m;

    for (i = 0; i < n; i++) {
        for (j = 0; j < n; j++) {
            if (i == j) {
                g->edges[i][j] = 0;
            }
            else {
                g->edges[i][j] = INT_MAX;
            }
        }
    }
}

void addEdge(Graph* g, int u, int v, int cost) {
    if (u >= 0 && u < g->n && v >= 0 && v < g->n) {
        g->edges[u][v] = cost;
        g->edges[v][u] = cost;
    }
}

int Prim(Graph g) {
    int dist[MAX_VERTICES];
    int visited[MAX_VERTICES];
    int totalCost = 0;
    int i, count, u, v;

    for (i = 0; i < g.n; i++) {
        dist[i] = INT_MAX;
        visited[i] = 0;
    }

    dist[0] = 0;

    for (count = 0; count < g.n; count++) {
        u = -1;
        for (i = 0; i < g.n; i++) {
            if (!visited[i] && (u == -1 || dist[i] < dist[u])) {
                u = i;
            }
        }

        if (dist[u] == INT_MAX) {
            return -1;
        }

        visited[u] = 1;
        totalCost += dist[u];

        for (v = 0; v < g.n; v++) {
            if (!visited[v] && g.edges[u][v] < dist[v]) {
                dist[v] = g.edges[u][v];
            }
        }
    }

    return totalCost;
}

void printGraph(Graph g) {
    int i, j;
    printf("图的邻接矩阵(%d个顶点):\n", g.n);
    printf("     ");
    for (i = 0; i < g.n; i++) {
        printf("%4d ", i);
    }
    printf("\n");
    for (i = 0; i < g.n; i++) {
        printf("%4d ", i);
        for (j = 0; j < g.n; j++) {
            if (g.edges[i][j] == INT_MAX) {
                printf("  ∞ ");
            }
            else {
                printf("%4d ", g.edges[i][j]);
            }
        }
        printf("\n");
    }
}

int main() {
    printf("========== 测试用例1:连通图 ==========\n");
    Graph g1;
    initGraph(&g1, 4, 5);

    addEdge(&g1, 0, 1, 5);
    addEdge(&g1, 0, 2, 8);
    addEdge(&g1, 0, 3, 3);
    addEdge(&g1, 1, 2, 6);
    addEdge(&g1, 2, 3, 7);

    printGraph(g1);
    int result1 = Prim(g1);
    printf("最小生成树总花费: %d\n", result1);

    printf("\n");

    printf("========== 测试用例2:不连通图 ==========\n");
    Graph g2;
    initGraph(&g2, 4, 3);

    addEdge(&g2, 0, 1, 5);
    addEdge(&g2, 1, 2, 6);

    printGraph(g2);
    int result2 = Prim(g2);
    printf("最小生成树总花费: %d (返回-1表示不连通)\n", result2);

    printf("\n");

    printf("========== 测试用例3:手动输入 ==========\n");
    printf("请输入城市数量 n 和道路数量 m:");
    int n, m;
    scanf("%d %d", &n, &m);

    Graph g3;
    initGraph(&g3, n, m);

    printf("请输入 %d 条道路,格式:城市1 城市2 花费(城市编号从0到%d)\n", m, n - 1);
    for (int i = 0; i < m; i++) {
        int u, v, cost;
        scanf("%d %d %d", &u, &v, &cost);
        addEdge(&g3, u, v, cost);
    }

    printGraph(g3);
    int result3 = Prim(g3);
    if (result3 == -1) {
        printf("图不连通,无法实现村村通!\n");
    }
    else {
        printf("最小生成树总花费:%d\n", result3);
    }

    printf("\n按任意键退出...");
    getchar();
    getchar();

    return 0;
}

时间复杂度

O(n²)

空间复杂度

O(n²)

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

  • 操作系统:Windows 11
  • 编程语言:C++
  • 开发工具:[Visual Studio Insiders]
  • 编译器:MSVC

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

以下请根据实际情况编写

题目1:图的创建

本机运行截图
055e8fa8f256272f0b5fe0afea2d3875

题目2:图的遍历

本机运行截图
6f3624a32c954ace8ec91f300d6f6c57

题目3:图着色问题

本机运行截图
f7004598e2ae40319386e5164af3172f

PTA提交截图
a0caf6a6645ee7fb8c9654e6d65aa6b5

题目4:公路村村通

本机运行截图
35667a5baeff69186f075caece901049

PTA提交截图
048bdf201a8d7c1d3710ef025c8c2b1a


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

以下请根据实际情况编写

遇到的问题及解决方法:

  1. 问题:邻接矩阵初始化时忘记对称赋值,导致输出矩阵不对称
    • 解决方法:添加 G->arcs[j][i] = w,确保无向图邻接矩阵对称
  2. 问题:BFS遍历结果与预期不符
    • 解决方法:使用打断点调试,发现是队列操作顺序问题,修正后结果正确

实验体会和收获:

  • 学会了使用邻接表存储图,理解了邻接矩阵和邻接表的适用场景。
  • 理解了Prim算法求解最小生成树的核心思想。
  • 掌握了邻接矩阵存储图的方法,理解了无向图矩阵的对称性。

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

posted on 2026-06-08 15:44  林宸葳  阅读(10)  评论(0)    收藏  举报