实验5:图
集美大学课程实验报告-实验5:图
| 项目名称 | 内容 |
|---|---|
| 课程名称 | 数据结构 |
| 班级 | 网安2411 |
| 指导教师 | 郑如滨 |
| 学生姓名 | 于鸿硕 |
| 学号 | 202421336018 |
| 实验项目名称 | 实验5:图 |
| 上机实践日期 | |
| 上机实践时间 | 2学时 |
一、目的(本次实验所涉及并要求掌握的知识点)
-
掌握图的基本操作方法 理解图的特性 熟练掌握两种遍历方式 简单掌握图运用至实际情况中的方法
二、实验内容与设计思想
#include <iostream>
#include<cstring>
#include<vector>
#include<queue>
//图结构定义
const int MAX_VERTEX_NUM = 100;
struct MGraph {
char vexs[MAX_VERTEX_NUM];
int arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
int vexnum, arcnum;
};
using namespace std;
return 0;
任务一、二:图的创建与遍历
//伪代码:
初始化图:
定义图的邻接矩阵的行列长度;
给邻接矩阵附值“A B C...”;
初始化数组元素为0;
在有值区域给邻接矩阵赋1;
输出图:
嵌套循环
输出0 1 值;
DFS:
检验是否已遍历过;
把图存入result中;
嵌套循环遍历;
BFS:
前序操作与DFS基本相同,区别点:
起始顶点入队;标记已访问节点;
当队列不为空时:
获取队头值,队头出队;
嵌套循环访问未访问节点:
邻接节点入队;
//代码:
#include<iostream>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
//图结构定义
const int MAX_VERTEX_NUM = 100;
struct MGraph {
char vexs[MAX_VERTEX_NUM];
int arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
int vexnum, arcnum;
};
//初始化图
void InitGraph(MGraph& G) {
G.vexnum = 6;
G.arcnum = 9;
G.vexs[0] = 'A';
G.vexs[1] = 'B';
G.vexs[2] = 'C';
G.vexs[3] = 'D';
G.vexs[4] = 'E';
G.vexs[5] = 'F';
memset(G.arcs, 0, sizeof(G.arcs));
G.arcs[0][1] = 1; G.arcs[1][0] = 1;
G.arcs[0][4] = 1; G.arcs[4][0] = 1;
G.arcs[1][4] = 1; G.arcs[4][1] = 1;
G.arcs[1][5] = 1; G.arcs[5][1] = 1;
G.arcs[2][3] = 1; G.arcs[3][2] = 1;
G.arcs[2][5] = 1; G.arcs[5][2] = 1;
G.arcs[3][5] = 1; G.arcs[5][3] = 1;
}
//输出图
void PrintGraph(const MGraph& G)
{
for (int i = 0;i < G.vexnum;i++)
{
for (int j = 0;j < G.vexnum;j++)
{
cout << G.arcs[i][j] << " ";
}
cout << endl;
}
}
//深度优先辅助函数
void DFS(const MGraph& G, int v, bool visited[], vector<char>& result)
{
visited[v] = true;
result.push_back(G.vexs[v]);
for (int i = 0;i < G.vexnum;i++)
{
if (G.arcs[v][i] && !visited[i])
{
DFS(G, i, visited, result);
}
}
}
//深度优先函数
vector<char>DFSTranverse(const MGraph& G, char start)
{
bool visited[MAX_VERTEX_NUM];
memset(visited, false, sizeof(visited));
vector<char> result;
int startIndex = -1;
for (int i = 0;i < G.vexnum;i++)
{
if (G.vexs[i] == start)
{
startIndex = i;
break;
}
}
if (startIndex != -1)DFS(G, startIndex, visited, result);
return result;
}
//广度优先
vector<char> BFSTraverse(const MGraph& G, char start) {
bool visited[MAX_VERTEX_NUM];
memset(visited, false, sizeof(visited));
vector<char> result;
queue<int> q;
int startIndex = -1;
for (int i = 0; i < G.vexnum; ++i) {
if (G.vexs[i] == start) {
startIndex = i;
break;
}
}
if (startIndex != -1) {
q.push(startIndex);
visited[startIndex] = true;
while (!q.empty()) {
int v = q.front();
q.pop();
result.push_back(G.vexs[v]);
for (int i = 0; i < G.vexnum; ++i) {
if (G.arcs[v][i] && !visited[i]) {
q.push(i);
visited[i] = true;
}
}
}
}
return result;
}
//主函数
int main() {
MGraph G;
InitGraph(G);
cout << "图的邻接矩阵:" << endl;
PrintGraph(G);
vector<char>dfsResultA = DFSTranverse(G, 'A');
cout << "自A进行DFS:";
for (char c : dfsResultA) {
cout << c;
}
cout << endl;
vector<char>dfsResultF = DFSTranverse(G, 'F');
cout << "自F进行DFS:";
for (char c : dfsResultF) {
cout << c;
}
cout << endl;
vector<char>bfsResultF = BFSTraverse(G, 'F');
cout << "自F进行BFS:";
for (char c : bfsResultF)
{
cout << c;
}
cout << endl;
return 0;
}
任务三:图着色问题
//伪代码:
主函数:
输入vek;输入uv并存入边集;
输入N;
对N个方案for循环:
输入颜色,插入颜色集;
如果颜色树!=K 则false;
else 提取边集
if(边集颜色==)false并break;
cout<<判断True or false<<yes or no;
//代码:
#include<iostream>
#include<vector>
#include<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 u,v;
cin>>u>>v;
edges.emplace_back(u,v);
}
int N;
cin>>N;
for(int i=0;i<N;i++){
vector<int> colors(V+1);
bool valid=true;
set<int> usedColors;
for(int j=1;j<=V;j++){
cin>>colors[j];
usedColors.insert(colors[j]);
}
if(usedColors.size()!=K)valid=false;
else{
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;
}
任务四:公路村村通
//伪代码:
1.设计两个查验函数,分别用于检查两个城镇是否已连接和按路径权重大小排序(升序);
2.main函数{
定义并输入城镇数和可通路数;
将路径按照权重大小排序;
在边集内从最小路径循环:
如最小路径所对应的两个节点未联通:
将这两个节点标记为联通;计数++;总花费+=单权重;
如所有节点已联通(计数==n-1) break;
如果计数!=n-1(所有城镇未被联通)cout<<-1<<endl;
else cout<<总花费;
}
//代码
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
struct Edge{
int u,v,w;
Edge(int u,int v,int w):u(u),v(v),w(w){}
bool operator<(const Edge& other)const{
return w < other.w;
}
};
class UnionFind{
private:
vector<int> parent;
public:
UnionFind(int size){
parent.resize(size+1);
for(int i=1;i<=size;i++){
parent[i]=i;
}
}
int find(int x){
if(parent[x]!=x){
parent[x]=find(parent[x]);
}
return parent[x];
}
void unite(int x,int y){
int rootX=find(x);
int rootY=find(y);
if(rootX!=rootY)parent[rootY]=rootX;
}
bool connected(int x,int y){
return find(x)==find(y);
}
};
int main(){
int n,m;
cin>>n>>m;
vector<Edge> edges;
for(int i=0;i<m;i++){
int u,v,w;
cin>>u>>v>>w;
edges.emplace_back(u,v,w);
}
sort(edges.begin(),edges.end());
UnionFind uf(n);
int totalcost=0;
int edgecount=0;
for(const Edge& edge:edges){
if(!uf.connected(edge.u,edge.v)){
uf.unite(edge.u,edge.v);
totalcost+=edge.w;
edgecount++;
if(edgecount==n-1)break;
}
}
if(edgecount!=n-1)cout<<-1<<endl;
else cout<<totalcost<<endl;
return 0;
}
三、实验使用环境(本次实验所使用的平台和相关软件)
以下请根据实际情况编写
- 操作系统:Windows 11
- 编程语言:C++
- 开发工具:visual studio2022 &pintia
- 编译器:g++
四、实验步骤和调试过程(实验步骤、测试数据设计、测试结果分析)
任务一、二:图的创建与遍历
本机运行截图

任务三:图着色问题
PTA提交截图

任务四:公路村村通
PTA提交截图

五、实验小结(实验中遇到的问题及解决过程、实验体会和收获)
遇到的问题及解决方法:
vector和memset等太难 用csdn查使用方法解决。
还有两种递归的理解 通过实验课和ppt领会
在公路村村通问题中,难点集中于我提到的两个函数(权重升序排序和判断是否联通)的设计方法,这一点比较难我自己写不出来,后来也是查询得到。
实验体会和收获:
在本次实验中,我首先理解了图类型数据结构性质,在我的任务一二中通过邻接矩阵的方式创建了图并对其进行操作。在对两种遍历方式的实验中,我发现DFS(深度优先)采用嵌套递归的遍历方式,类似于树的递归遍历思想,实际操作中可能具有先序树的相关性质,但非连通图则需另外考虑;而BFS(广度优先)则借用队列进行层级优先遍历,类似于树的层级遍历。
问题解决中,在图的着色问题上引入了边集这一概念,发现在连通无向图中很实用,通过访问边就能轻松访问各个顶点,有向图或许可以用vector来使用(?)。
同时,也通过公路村村通问题理解了最小生成树在实际中的运用,这种插入逻辑方式能以很小的时空复杂度实现最优方法的创建。
浙公网安备 33010602011771号