【数据结构与算法】【C++】图的邻接矩阵实验报告(五)

目录

阅读建议:

一、实验目的

二、实验内容

三、实验过程

四、代码结构

五、测试结果


阅读建议:

1.实验的软硬件环境要求:

(1)硬件环境要求:PC机
(2)软件环境要求:Windows 环境下的 Microsoft Visual Studio

2.该实验采用了头文件(.h)和源文件(.cpp)相结合的形式。


一、实验目的

1.熟练掌握图的邻接矩阵存储结构的实现;

2.熟练掌握图的基本操作算法实现,包括创建、遍历、连通性判断、最小生成树的求解等;

3.灵活使用图来解决具体的问题。


二、实验内容

1.用邻接矩阵实现无向网的存储,封装图基本操作算法,包括:

        (1)创建

        (2)顶点和边的增删、修改

        (3)深度优先和广度优先遍历; 

2.判断图的连通性,不连通则计算连通分量的个数;

3.实现Prim算法。


三、实验过程

1.全局数组变量

const int MaxSize = 10;
int visited[MaxSize] = { 0 };  // 全局数组变量 visited 初始化

2.构造函数---图的建立,接受一个顶点数组 ,顶点数量 和边数量 作为输入,然后使用这些信息初始化一个图的邻接矩阵。

工作原理:

  1. 首先,它根据输入的顶点和边数量初始化一些变量和数组。
  2. 然后,它使用输入的顶点数组初始化顶点数组。
  3. 接下来,它初始化邻接矩阵为全零,表示开始时没有边。
  4. 最后,它读取边的信息,并更新邻接矩阵以反映这些边。
template
MGraph::MGraph(T a[], int n, int e)
{
	int i, j, k;
	vertexNum = n;
	edgeNum = e;
	// 存储顶点
	for (i = 0; i < vertexNum; i++) {
		vertex[i] = a[i];
	}
	// 初始化邻接矩阵
	for (i = 0; i < vertexNum; i++) {
		for (j = 0; j < vertexNum; j++) {
			edge[i][j] = 0;
		}
	}
	// 依次输入每一条边
	for (k = 0; k < edgeNum; k++) {
		// 输入边依附的两个顶点的编号
		cin >> i >> j;
		// 置有边标志
		edge[i][j] = 1;
		edge[j][i] = 1;
	}
}

3.析构函数---图的销毁。

template
MGraph::~MGraph(){}

4.添加顶点。

工作原理:

  1. 首先,函数检查当前的顶点数量(vertexNum)是否已经达到最大值(MaxSize)。这是为了确保图没有达到其容量限制。
  2. 如果图已满(即vertexNum >= MaxSize),则函数输出一个错误消息“图已满!”,并返回false,表示添加顶点的操作失败。
  3. 如果图未满,则将新顶点的值存储在vertex数组的下一个位置(即vertex[vertexNum]),并将vertexNum增加1,以表示图中的顶点数量增加了。
  4. 最后,函数返回true,表示添加顶点的操作成功。
template
bool MGraph::addVertex(const T& newVertex) {
	if (vertexNum >= MaxSize) {
		cout << "图已满!" << endl;
		return false;
	}
	vertex[vertexNum++] = newVertex;
	return true;
}

5.删除顶点。

 工作原理:

  1. 首先,函数检查输入的顶点索引是否有效。如果索引小于0或大于等于顶点数量(vertexNum),则函数输出一个错误消息“输入无效!”,并返回false,表示删除顶点的操作失败。
  2. 如果索引有效,则进入下一步操作。
  3. 接下来,函数遍历邻接矩阵的每一行和每一列,并将与要删除顶点相关的元素值设置为0。这样做是为了标记该顶点与其相邻顶点之间的边已被删除。
  4. 最后,函数返回true,表示删除顶点的操作成功完成
template
bool MGraph::removeVertex(int vertexIndex) {
	if (vertexIndex < 0 || vertexIndex >= vertexNum) {
		cout << "输入无效!" << endl;
		return false;
	}
	for (int i = 0; i < vertexNum; i++) {
		edge[i][vertexIndex] = edge[vertexIndex][i] = 0;
	}
	return true;
}

6.添加边。

 工作原理:

  1. 首先,函数检查输入的索引是否有效。如果索引小于0或大于等于顶点数量(vertexNum),则函数输出一个错误消息“输入无效!”,并返回false,表示添加边的操作失败。
  2. 如果索引有效,则进入下一步操作。
  3. 接下来,函数将邻接矩阵中对应位置的值设置为1,表示在顶点i和顶点j之间存在一条边。由于这是一个无向图,因此同时设置edge[i][j]edge[j][i]为1。
  4. 最后,函数返回true,表示添加边的操作成功完成
template
bool MGraph::addEdge(int i, int j) {
	if (i < 0 || i >= vertexNum || j < 0 || j >= vertexNum) {
		cout << "输入无效!" << endl;
		return false;
	}
	edge[i][j] = edge[j][i] = 1;
	return true;
}

7.删除边。

 工作原理:

  1. 首先,函数检查输入的索引是否有效。如果索引小于0或大于等于顶点数量(vertexNum),或者在邻接矩阵中对应的值为0(表示没有边),则函数输出一个错误消息“输入无效!”,并返回false,表示删除边的操作失败。
  2. 如果索引有效,则进入下一步操作。
  3. 接下来,函数将邻接矩阵中对应位置的值设置为0,表示在顶点i和顶点j之间不存在边了。由于这是一个无向图,因此同时设置edge[i][j]edge[j][i]为0。
  4. 最后,函数返回true,表示删除边的操作成功完成
template
bool MGraph::removeEdge(int i, int j) {
	if (i < 0 || i >= vertexNum || j < 0 || j >= vertexNum || edge[i][j] == 0) {
		cout << "输入无效!" << endl;
		return false;
	}
	edge[i][j] = edge[j][i] = 0;
	return true;
}

8.深度优先遍历。

工作原理:

  1.  首先,函数输出当前顶点的值(通过cout << vertex[v];)。
  2. 然后,将当前顶点标记为已访问(通过visited[v] = 1;)。
  3. 接下来,函数遍历所有与当前顶点直接相连的未访问顶点(通过for循环)。对于每一个未访问的相邻顶点j,如果该相邻顶点与当前顶点之间存在边(即edge[v][j] == 1),则递归调用DFTraveres(j),继续进行深度优先遍历
template
void MGraph::DFTraveres(int v)
{
	cout << vertex[v];
	visited[v] = 1;
	for (int j = 0; j < vertexNum; j++) {
		if (edge[v][j] == 1 && visited[j] == 0) {
			DFTraveres(j);
		}
	}
}

9.广度优先遍历。

 工作原理:

  1. 首先,函数输出当前顶点的值(通过cout << vertex[v];)。
  2. 然后,将当前顶点标记为已访问(通过visited[v] = 1;)。
  3. 接下来,将已访问的顶点加入到队列中(通过Q[++rear] = v;)。队列使用数组Q实现,其中frontrear分别表示队头和队尾的索引。
  4. 进入循环,当队列非空时(通过while (front!=rear)),执行以下操作:将队头元素出队并送到变量w中(通过w = Q[++front];)。
  • 遍历与当前顶点w直接相连的所有未访问顶点(通过for循环)。对于每一个未访问的相邻顶点j,如果该相邻顶点与当前顶点之间存在边(即edge[w][j] == 1),并且该相邻顶点尚未被访问(即visited[j] == 0),则将该相邻顶点的值输出(通过cout << vertex[j];),将其标记为已访问(通过visited[j] = 1;),并将其加入到队列中(通过Q[++rear] = j;)。
template
void MGraph::BFTraveres(int v)
{
	// 采用顺序队列
	int w, j, Q[MaxSize];
	// 初始化队列
	int front = -1, rear = -1;
	cout << vertex[v];
	visited[v] = 1;
	// 被访问顶点入队
	Q[++rear] = v;
	// 当队列非空时
	while (front!=rear){
		// 将队头元素出队并送到v中
		w = Q[++front];
		for (j = 0; j < vertexNum; j++) {
			if (edge[w][j] == 1 && visited[j] == 0) {
				cout << vertex[j];
				visited[j] = 1;
				Q[++rear] = j;
			}
		}
	}
}

10.判断图的连通性,不连通则计算连通分量的个数。

 工作原理:

  1.  函数首先初始化一个变量connectedComponents为0,用于记录连通分量的数量。
  2. 然后,函数遍历所有的顶点(通过for循环)。对于每一个未访问的顶点i(即visited[i] == 0),如果该顶点尚未被访问,则递归调用DFTraveres(i)进行深度优先遍历。在遍历过程中,所有被访问的顶点都会被标记为已访问(通过visited[i] = 1;)。
  3. 每次进行深度优先遍历后,连通分量的数量增加1(通过connectedComponents++)。
  4. 遍历完成后,根据连通分量的数量判断图是否是连通的。如果连通分量的数量为1,则图是连通的,输出相应的消息;否则,图是非连通的,输出连通分量的数量。
  5. 最后,函数返回连通分量的数 
template
int MGraph::isConnected() {
	int connectedComponents = 0; 
	for (int i = 0; i < vertexNum; i++) {
		if (visited[i] == 0) {
			DFTraveres(i);
			connectedComponents++;
		}
	}
	// 如果连通分量数为1,则图是连通的,否则图是非连通的  
	if (connectedComponents == 1) {
		cout << "图是连通的。" << endl;
	}
	else {
		cout << "图是非连通的,连通分量数为: " << connectedComponents << endl;
	}
	return connectedComponents;
}

11.计算顶点的度。

 工作原理:

  1.  ​​​​​​​函数首先声明一个整型数组degree,用于存储每个顶点的度。数组的大小为MaxSize,这是预先定义的常数,表示图中顶点的最大数量。
  2. 然后,函数初始化度数组为0,确保每个顶点的度初始化为0。
  3. 接下来,函数遍历所有的顶点(通过两个嵌套的for循环)。对于每对顶点i和j,如果边存在(即edge[i][j] == 1),则将顶点i的度增加1(通过degree[i]++)。
  4. 最后,函数遍历所有的顶点,并输出每个顶点的度(通过cout语句)
template
void MGraph::calculateDegree() {
	// 存储每个顶点的度
	int degree[MaxSize];
	// 初始化度数组为0
	for (int i = 0; i < MaxSize; i++) {
		degree[i] = 0;
	}
	// 计算每个顶点的度  
	for (int i = 0; i < vertexNum; i++) {
		for (int j = 0; j < vertexNum; j++) {
			if (edge[i][j] == 1) {
				degree[i]++;
			}
		}
	}
	// 输出每个顶点的度
	for (int i = 0; i < vertexNum; i++) {
		cout << "顶点 " << vertex[i] << " : " << degree[i] << endl;
	}
}

12.Prim算法,用于求解最小生成树问题。

工作原理:

  1.   函数首先声明了一些变量ijk,以及两个整型数组adjvexlowcost,分别用于存储邻接点和最小权重。
  2.  然后,函数初始化辅助数组。对于每个顶点i,lowcost[i]被初始化为从顶点v到顶点i的边的权重(通过edge[v][i]),而adjvex[i]被初始化为顶点v。
  3.  接下来,函数将顶点v加入集合U,并将其最小权重设置为0。
  4.  然后,函数进入一个循环,迭代n-1次(通过for (k = 1; k < vertexNum; k++))。在每次迭代中,函数执行以下操作:
  5. 寻找具有最小权重的边对应的邻接点j(通过调用MinEdge(lowcost, vertexNum)函数)。
  • 输出起点和终点以及权重(通过cout语句)。
  • 将顶点j加入集合U,并将其最小权重设置为0。
  • 调整辅助数组。对于每个顶点i,如果存在一条从i到j的边且权重小于当前的最小权重(通过edge[i][j] < lowcost[i]),则更新最小权重和邻接点(通过lowcost[i] = edge[i][j]adjvex[i] =j)。
template
void MGraph::Prim(int v)
{
	int i, j, k;
	int adjvex[MaxSize], lowcost[MaxSize];
	// 初始化辅助数组
	for (i = 0; i < vertexNum; i++) {
		lowcost[i] = edge[v][i];
		adjvex[i] = v;
	}
	// 将顶点v加入集合U
	lowcost[v] = 0;
	// 迭代n-1次
	for (k = 1; k < vertexNum; k++) {
		// 寻找最短边的邻接点 j
		j = MinEdge(lowcost, vertexNum);
		// 起点-终点 权重
		cout << adjvex[j] << "-" << j << " " << lowcost[j] << endl;
		// 顶点 j 加入集合U
		lowcost[j] = 0;
		// 调整辅助数组
		for (i = 0; i < vertexNum; i++) {
			if (edge[i][j] < lowcost[i]) {
				lowcost[i] = edge[i][j];
				adjvex[i] = j;
			}
		}
	}
}

13.寻找最短边的邻接点。

工作原理:

  1.  函数首先声明了几个变量mintemp, 和 k,分别用于存储当前最小权重、最小权重对应的顶点索引和循环计数器。
  2. 然后,函数初始化min为一个大值(例如100000000),表示初始时没有找到更小的权重。
  3. 接下来,函数遍历整个最小权重数组(通过for循环)。对于每个顶点k,如果当前最小权重小于min且不为0(通过lowcost[k] < min && lowcost[k] != 0),则更新min为当前最小权重,并记录下对应的顶点索引temp
  4. 最后,函数返回最小权重对应的顶点索引temp
template
int MGraph::MinEdge(int lowcost[MaxSize], int vertexNum)
{
	int min = 100000000;
	int temp = 0, k;
	for (k = 0; k < vertexNum; k++)
	{
		if (lowcost[k] < min && lowcost[k] != 0)
		{
			min = lowcost[k];
			temp = k;
		}
	}
	return temp;
}

14.主函数。

int main()
{
	int i;
	char ch[] = { 'A','B','C','D'};
	cout << "【依次输入每一条边的两个顶点的编号】:\n";
	MGraph MG{ch, 4, 4};  // 建立具有4个顶点 4条边的无向图

	for (i = 0; i < MaxSize; i++) {
		visited[i] = 0;
	}
	cout << "\n【深度优先遍历序列】:";
	MG.DFTraveres(0);  // 从顶点0出发进行深度优先遍历

	for (i = 0; i < MaxSize; i++) {
		visited[i] = 0;
	}
	cout << "\n【广度优先遍历序列】:";
	MG.BFTraveres(0);  // 从顶点0出发进行广度优先遍历
	cout << endl;

	for (i = 0; i < MaxSize; i++) {
		visited[i] = 0;
	}
	cout << "\n【判断图的连通性】:";
	MG.isConnected();
	
	for (i = 0; i < MaxSize; i++) {
		visited[i] = 0;
	}
	cout << "\n【各顶点的度】:\n";
	MG.calculateDegree();

	for (i = 0; i < MaxSize; i++) {
		visited[i] = 0;
	}
	cout << "\n【Prim算法的最小生成树】:(起点-终点 权重)\n";
	MG.Prim(0);  // 从顶点0出发进行Prim算法

	return 0;
}

四、代码结构

【数据结构与算法】【C++】图的邻接矩阵实验报告(五)_第1张图片


五、测试结果

【数据结构与算法】【C++】图的邻接矩阵实验报告(五)_第2张图片


        完整代码链接:https://download.csdn.net/download/weixin_73286497/88758704

        希望大家可以在该篇实验报告中有所收获,同时也感谢各位大佬的支持。文章如有任何问题请在评论区留言斧正,鸿蒙会尽快回复您的建议!

你可能感兴趣的:(数据结构,#,C++,c++,数据结构,算法,广度优先,深度优先,visualstudio)