图
集美大学课程实验报告-实验3:栈、队列与递归
| 项目名称 | 内容 |
|---|---|
| 课程名称 | 数据结构 |
| 班级 | 网安2512 |
| 实验项目名称 | 图 |
| 上机实践日期 | 5月28日 |
| 上机实践时间 | 2学时 |
一、目的(本次实验所涉及并要求掌握的知识点)
以下内容请根据实际情况编写
- 学会创建图(邻接矩阵),掌握在图上的基本操作。
- 掌握图遍历算法:DFS与BFS。
- 掌握编写最小生成树算法。
二、实验内容与设计思想
- 任务书:题目1—图的创建
- 任务书:题目2—图的遍历(DFS、BFS)
- PTA:图着色问题
- AI任务:公路村村通(最小生成树)
题目1:图的创建
函数相关伪代码
vex[] //点集合
arcs[][] //边集合
LocateVex() //先输入点
然后进行点与边的输入
函数代码
#include<iostream>
#include<string>
using namespace std;
#define MAXVEXNUM 100
//点,边
typedef int ArcCell;
typedef char VexType;
typedef struct {
VexType vexs[MAXVEXNUM];
ArcCell arcs[MAXVEXNUM][MAXVEXNUM];
int vexNum, arcNum;
} MyGraph;
void CreateDGraphFromConsole(MyGraph& G, int vexNum, int arcNum);
int LocateVex(MyGraph* G, int value)
{
for (int i = 0; i < G->vexNum; i++)
{
if (value == G->vexs[i])
return i;
}
return -1;
}
void CreateDGraphFromConsole(MyGraph& G, int vexNum, int arcNum)
{
G.vexNum = vexNum;
G.arcNum = arcNum;
memset(G.vexs, 0, sizeof(G.vexs));
memset(G.arcs, 0, sizeof(G.arcs));
for (size_t i = 0; i < G.vexNum; i++)
{
cin >> G.vexs[i];
}
char x, y;
ArcCell value;
for (size_t i = 0; i < G.arcNum; i++)
{
cin >> x >> y >> value;
int x1 = LocateVex(&G, x);
int y1 = LocateVex(&G, y);
G.arcs[x1][y1] = value;
G.arcs[y1][x1] = value;
}
}
int main()
{
MyGraph G;
int vexNum, arcNum;
cout << "请输入顶点数和边数:";
cin >> vexNum >> arcNum;
cout << "请输入 " << vexNum << " 个顶点值(字符,连续输入或空格分隔):" << endl;
CreateDGraphFromConsole(G, vexNum, arcNum);
// 打印邻接矩阵
cout << "\n邻接矩阵如下:" << 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;
}
return 0;
}
题目2:图的遍历(DFS、BFS)
函数相关伪代码
DFS:先访问邻接点,等到头或是访问过,就用递归来返回上一个节点,然后找到未访问的节点,直到所有节点都被访问
BFS:先处理顶点,然后与顶点相连的邻接节点入队列,然后访问这些邻接点的邻接点,直到所有节点都被访问
函数代码
#include <iostream>
#include <string>
#include <cstdio>
#include <queue>
using namespace std;
#define INTMAX 32767
#define MAXVEXNUM 100
//点,边
typedef char VexType;
typedef int ArcCell;
typedef struct
{
VexType vexs[MAXVEXNUM]; // 点的集合
ArcCell arcs[MAXVEXNUM][MAXVEXNUM]; // 边的集合
int vexNum, arcNum;
} MyGraph;
MyGraph* CreateDefaultGraph1() // 创建无向图
{
MyGraph* G = new MyGraph;
// 初始化邻接矩阵
memset(G->arcs, 0, sizeof(G->arcs));
memset(G->vexs, 0, sizeof(G->vexs));
G->vexNum = 6;
G->arcNum = 7;
G->vexs[0] = 'a';
G->vexs[1] = 'b';
G->vexs[2] = 'c';
G->vexs[3] = 'd';
G->vexs[4] = 'e';
G->vexs[5] = 'f';
G->arcs[0][1] = G->arcs[1][0] = 1;
G->arcs[0][4] = G->arcs[4][0] = 1;
G->arcs[1][4] = G->arcs[4][1] = 1;
G->arcs[1][5] = G->arcs[5][1] = 1;
G->arcs[2][3] = G->arcs[3][2] = 1;
G->arcs[2][5] = G->arcs[5][2] = 1;
G->arcs[3][5] = G->arcs[5][3] = 1;
return G;
}
MyGraph* CreateDefaultGraph2() // 创建有向图
{
MyGraph* G = new MyGraph;
// 初始化邻接矩阵
memset(G->arcs, 0, sizeof(G->arcs));
memset(G->vexs, 0, sizeof(G->vexs));
G->vexNum = 5;
G->arcNum = 7;
G->vexs[0] = '1';
G->vexs[1] = '2';
G->vexs[2] = '3';
G->vexs[3] = '4';
G->vexs[4] = '5';
G->arcs[0][1] = 10;
G->arcs[0][3] = 30;
G->arcs[0][4] = 100;
G->arcs[1][2] = 50;
G->arcs[2][4] = 10;
G->arcs[3][2] = 20;
G->arcs[3][4] = 60;
return G;
}
// 根据value节点值返回节点位置,没找到则返回-1
int LocateVex(MyGraph* G, VexType value)
{
for (int i = 0; i < G->vexNum; i++)
{
if (value == G->vexs[i])
return i;
}
return -1;
}
void printGraph(MyGraph* G)
{
// 输出图的基本信息(几个点、几条边)
cout << "图中的点数:" << G->vexNum << ", 边数:" << G->arcNum << 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] << "\t";
}
cout << endl;
}
}
void BFS(MyGraph* G, VexType v, bool* visited)
{
queue<int> q;
int startIdx = LocateVex(G, v);
q.push(startIdx);
visited[startIdx] = true;
while (!q.empty())
{
int currentIdx = q.front();
q.pop();
cout << G->vexs[currentIdx] << " ";
// 遍历所有邻接点
for (int i = 0; i < G->vexNum; i++)
{
if (G->arcs[currentIdx][i] != 0 && !visited[i])
{
visited[i] = true;
q.push(i);
}
}
}
}
void BFSTravse(MyGraph* G) {
bool* visited = new bool[G->vexNum](); // 初始化为 false
cout << "BFS遍历结果(非连通图):";
for (int i = 0; i < G->vexNum; i++) {
if (!visited[i]) {
BFS(G, G->vexs[i], visited);
}
}
cout << endl;
delete[] visited;
}
void DFS(MyGraph* G, VexType v, bool* visited)
{
int index = LocateVex(G, v);
cout << G->vexs[index] << " ";
visited[index] = true;
for (int i = 0; i < G->vexNum; i++)
{
if (G->arcs[index][i] != 0)
{
if (!visited[i])
{
DFS(G, G->vexs[i], visited);
}
}
}
}
void DFSTravse(MyGraph* G) {
bool* visited = new bool[G->vexNum](); // 初始化为 false
cout << "DFS遍历结果(非连通图):";
for (int i = 0; i < G->vexNum; i++) {
if (!visited[i]) {
DFS(G, G->vexs[i], visited); // 修正:传入顶点值,而不是索引 i
}
}
cout << endl;
delete[] visited;
}
int main()
{
// 测试无向图
cout << "========== 测试无向图 ==========" << endl;
MyGraph* myGraph = CreateDefaultGraph1();
printGraph(myGraph);
// DFS 遍历连通图(从 a 开始)
cout << "\nDFS遍历(从a开始):";
bool* visited = new bool[myGraph->vexNum]();
DFS(myGraph, 'a', visited);
cout << endl;
// DFS 遍历非连通图
DFSTravse(myGraph);
// BFS 遍历非连通图
BFSTravse(myGraph);
delete[] visited;
// 测试有向图
cout << "\n========== 测试有向图 ==========" << endl;
MyGraph* myGraph2 = CreateDefaultGraph2();
printGraph(myGraph2);
// BFS 遍历有向图
BFSTravse(myGraph2);
// DFS 遍历有向图
DFSTravse(myGraph2);
return 0;
}
题目3:图的着色问题
函数相关伪代码
先用数组储存所有边
用给定数据中相同颜色的节点创建另一个数组
然后比较俩数组的边是否有相同边
如果有相同边则输出NO
函数代码
#include <iostream>
#include <set>
using namespace std;
#define MAXV 510 // 最大顶点数
#define MAXE 250000 // 最大边数(完全图)
int main() {
int V, E, K;
cin >> V >> E >> K;
// 用数组存储边
int u[MAXE], v[MAXE]; // 每条边的两个端点
for (int i = 0; i < E; i++) {
cin >> u[i] >> v[i];
}
int N;
cin >> N;
for (int i = 0; i < N; i++) {
int colors[MAXV]; // 存储每个顶点的颜色
set<int> colorSet;
// 读入着色方案
for (int j = 1; j <= V; j++) {
cin >> colors[j];
colorSet.insert(colors[j]);
}
// 条件1:颜色数必须等于K
if (colorSet.size() != K) {
cout << "No" << endl;
continue;
}
// 条件2:检查每条边的两个端点颜色是否相同
bool valid = true;
for (int j = 0; j < E; j++) {
if (colors[u[j]] == colors[v[j]]) {
valid = false;
break;
}
}
cout << (valid ? "Yes" : "No") << endl;
}
return 0;
}
题目4:公路村村通(最小生成树)
函数相关伪代码
typedef struct
{
int vex[]; // 顶点列表(存储顶点编号 1..n)
int edges[][]; // 邻接矩阵,存储边权
} MyGraph;
CreateGraph9()//创建一个图
Prim()
{
从1开始构建
初始化 lowcost:顶点 1 到其他顶点的距离
再加入 n-1 个顶点
选择当前树到未加入顶点中最小的边
若找不到最小边,返回 -1
将顶点 u 加入生成树,累加
更新 lowcost
}
函数代码
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#define MAXN 1005
#define INF INT_MAX
typedef struct {
int vex[MAXN]; // 顶点编号(实际就是 1..n)
int edges[MAXN][MAXN]; // 邻接矩阵,存储边权
int n, m; // 顶点数,边数
} MyGraph;
// 创建并初始化图
void CreateGraph(MyGraph *G, int n, int m) {
G->n = n;
G->m = m;
// 初始化顶点编号
for (int i = 1; i <= n; i++) {
G->vex[i] = i;
}
// 初始化邻接矩阵
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (i == j)
G->edges[i][j] = 0;
else
G->edges[i][j] = INF;
}
}
// 读入 m 条边
int u, v, cost;
for (int i = 0; i < m; i++) {
scanf("%d %d %d", &u, &v, &cost);
G->edges[u][v] = cost;
G->edges[v][u] = cost; // 无向图
}
}
// Prim 算法求最小生成树总权值,若不连通返回 -1
int Prim(MyGraph *G) {
int n = G->n;
int lowcost[MAXN]; // 记录当前生成树到每个顶点的最小边权
int visited[MAXN]; // 标记顶点是否已在生成树中
int sum = 0;
// 初始化
for (int i = 1; i <= n; i++) {
lowcost[i] = G->edges[1][i]; // 从顶点 1 出发
visited[i] = 0;
}
visited[1] = 1; // 顶点 1 加入生成树
// 还需要加入 n-1 个顶点
for (int count = 1; count < n; count++) {
int minCost = INF;
int u = -1;
// 选择最小边
for (int i = 1; i <= n; i++) {
if (!visited[i] && lowcost[i] < minCost) {
minCost = lowcost[i];
u = i;
}
}
if (u == -1) { // 找不到最小边,说明图不连通
return -1;
}
// 将 u 加入生成树
visited[u] = 1;
sum += minCost;
// 更新 lowcost
for (int v = 1; v <= n; v++) {
if (!visited[v] && G->edges[u][v] < lowcost[v]) {
lowcost[v] = G->edges[u][v];
}
}
}
return sum;
}
int main() {
int n, m;
scanf("%d %d", &n, &m);
MyGraph G;
CreateGraph(&G, n, m);
int result = Prim(&G);
printf("%d\n", result);
return 0;
}
三、实验使用环境(本次实验所使用的平台和相关软件)
以下请根据实际情况编写
- 操作系统:Windows 11专业版
- 编程语言:C++
- 开发工具:[Visual Studio 2025]
四、实验步骤和调试过程(实验步骤、测试数据设计、测试结果分析)
题目1:题目1—图的创建
本机运行截图

PTA提交截图
无
题目2:题目2—图的遍历(DFS、BFS)
本机运行截图

PTA提交截图
无
题目3:图着色问题
本机运行截图

PTA提交截图

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

PTA提交截图

五、实验小结(实验中遇到的问题及解决过程、实验体会和收获)
以下请根据实际情况编写
遇到的问题及解决方法:
- 问题:程序崩溃找不到原因,
- 解决方法:使用打断点进行调试的方法。
- 问题:代码缩进不规范。
- 解决方法:使用IDE的格式化工具修复代码。
实验体会和收获:
- 学会了如何搭建C++开发环境。
- 掌握了基本的代码调试方法。
- 掌握了Visual Studio调试功能的基本使用
浙公网安备 33010602011771号