普里姆算法求图(邻接矩阵存储)的最小生成树

——图的存储结构为: 邻接矩阵

具体算法思想和过程实现:
请前往B站,观看Up主 : 懒猫老师 的视频

视频1 : 《懒猫老师-数据结构-(42)最小生成树(Prim算法,普里姆算法,普利姆)》

视频2 : 《懒猫老师数据结构-(43)最小生成树(Prim算法的实现,普里姆算法,普利姆)》

视频1传送门
视频2传送门

附上本人实现的代码:
能成功运行,可能会存在有不足的地方,如果有,敬请指出,并多多指教。

// 用邻接矩阵来作为图的存储结构,用来实现求最小生成树。

#include<iostream>
using namespace std;

#define MVNum 100
#define Maxlnt 32767
#define OK 1

class AMGraph;
class AMGraph
{
private:
	char vexs[MVNum];          //定义一个顶点表;
	int arcs[MVNum][MVNum];    //定义一个邻接矩阵;
	int vexnum, arcnum;        // 定义图的当前点数和边数;


public:
	bool creat_graph();         //图的构造函数;
	int Locate_vex(char c);     //定位函数,确定顶点在顶点表当中的位置;
	void print();               //将图的顶点表和邻接矩阵都给打印出来。
	friend void Prim(AMGraph G);

};

//辅助数组中所需要用到的结构体:
typedef struct SE
{
	char adjvex;
	int lowcost;  //最小生成树中边所对应的最小权值;
};

int AMGraph::Locate_vex(char c)
{
	for (int i = 0; i < vexnum; i++)
	{
		if (c == vexs[i]) return i;
	}
	return -1; // -1表示顶点不存在顶点表当中;
}

bool AMGraph::creat_graph()
{
	cout << "请依次输入总顶点数和总边数:" << "\t";
	cin >> vexnum >> arcnum;  //输入总顶点数和总边数;
	//1. 完成对所有顶点的信息的输入。
	for (int i = 0; i < vexnum; i++)
	{
		cout << "请输入第" << i + 1 << "个顶点:" << "\t";
		cin >> vexs[i];
	}
	//2.对邻接矩阵进行初始化工作;将各条边的权值置为最大值;
	//对角线上的元素置为 0, 其它边的元素置为最大,即Maxlnt
	for (int i = 0; i < vexnum; i++)
	{
		for (int j = 0; j < vexnum; j++)
		{
			if (i == j) arcs[i][j] = 0;
			else arcs[i][j] = Maxlnt;
		}
	}
	//3.完成对邻接矩阵中各条边的赋值;
	for (int k = 0; k < arcnum; k++)
	{
		char v1, v2;
		int w;
		cout << "请分别输入第" << k + 1 << "条边的两个顶点和权值:" << "\t";
		cin >> v1 >> v2 >> w;
		int i = Locate_vex(v1);
		int j = Locate_vex(v2);
		//将边(v1,v2)和其对称边(v2,v1)的权值都值为w;
		arcs[i][j] = arcs[j][i] = w;
	}
	return OK;
}

void AMGraph::print()
{
	cout << endl << "顶点表的信息: " << endl;
	for (int i = 0; i < vexnum; i++)
	{
		cout << vexs[i] << " ";
	}
	cout << endl;
	cout << "邻接矩阵的信息:" << endl;
	for (int i = 0; i < vexnum; i++)
	{
		for (int j = 0; j < vexnum; j++)
		{
			if (arcs[i][j] == Maxlnt)
				cout << "∞" << "\t";
			else cout << arcs[i][j] << "\t";
			if (j == vexnum - 1)
				cout << endl;
		}
	}

}

int minEdge(SE* array, int n)
{
	int min = Maxlnt;
	int mark;
	for (int i = 0; i < n; i++)
	{
		if (array[i].lowcost != 0 && array[i].lowcost < min)
		{
			min = array[i].lowcost;
			mark = i;
		}
	}
	return mark;
}

void output_SMT(int k, SE* array,char c)
{
	cout << "(" << array[k].adjvex;
	cout << " , " << c << ")" << "\t" << array[k].lowcost << endl;
}

void Prim(AMGraph G)
{
	int start, k;
	char c;
	SE* shortEdge;
	shortEdge = new SE[G.vexnum];

	//1. 对初始辅助数组进行赋初值;
	cout << "请输入最小生成树的起点: " << "\t";
	cin >> c;
	start = G.Locate_vex(c);                                  //输入起点在顶点表当中的序号
	for (int i = 0; i < G.vexnum; i++)
	{
		shortEdge[i].lowcost = G.arcs[start][i];
		shortEdge[i].adjvex = G.vexs[start];
	}

	shortEdge[start].lowcost = 0;                             //将起点加入算法思想当中的U集合当中。
	for (int i = 0; i < G.vexnum - 1; i++)
	{
		k = minEdge(shortEdge, G.vexnum);                    //寻找最短边的邻接点的序号;
		output_SMT(k,shortEdge,G.vexs[k]);                   //输出最小生成树的路径;
		shortEdge[k].lowcost = 0;                            //将该顶点加入到算法思想当中的U集合当中;
		for (int j = 0; j < G.vexnum; j++)
		{
			if (G.arcs[k][j] < shortEdge[j].lowcost)
			{
				shortEdge[j].lowcost = G.arcs[k][j];
				shortEdge[j].adjvex = G.vexs[k];
			}
		}
	}
	delete[]shortEdge;
}


int main()
{
	int minEdge(SE * array, int n);
	void output_SMT(int k, SE * array);
	AMGraph p1;
	p1.creat_graph();
	p1.print();
	cout << endl;
	cout << "最小生成树路径为:" << endl;
	Prim(p1);
	return 0;
}

一、测试数据:

在这里插入图片描述
二、测试结果:
在这里插入图片描述
谢谢你的收看!

  • 3
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我来为您解答。 首先,我们需要了解一些基本概念: 1. 无向图:由若干个顶点和边组成,每条边连接两个顶点,没有方向。 2. 带权图:每条边都有一个权值。 3. 最小生成树:在一个连通的带权无向图中,找到一棵生成树,使得这棵树上所有边的权值之和最小。 4. 邻接矩阵:用一个二维数组来表示一个图,数组中的元素表示边的权值。 接下来,我们可以按照以下步骤来实现用普里姆算法最小生成树: 1. 定义图的结构: ``` #define MAXV 100 // 最大顶点数 #define INF 0x3f3f3f3f // 表示正无穷 // 边的结构体 struct Edge { int u, v; // 边的两个端点 int w; // 边的权值 }; // 图的结构体 struct Graph { int n, m; // n为顶点数,m为边数 int edges[MAXV][MAXV]; // 邻接矩阵存储图 }; ``` 2. 编写生成邻接矩阵算法: ``` void createGraph(Graph &G) { int u, v, w; memset(G.edges, INF, sizeof(G.edges)); // 初始化为无穷大 scanf("%d %d", &G.n, &G.m); for (int i = 1; i <= G.m; i++) { scanf("%d %d %d", &u, &v, &w); G.edges[u][v] = G.edges[v][u] = w; // 无向图,所以要对称存储 } } ``` 3. 编写普里姆算法: ``` void prim(Graph G, int start) { int dist[MAXV], vis[MAXV], parent[MAXV]; // dist数组用于存储当前点到树的距离,vis数组用于判断是否已经加入了树,parent数组用于存储当前点的父节点 // 初始化 for (int i = 1; i <= G.n; i++) { dist[i] = INF; vis[i] = 0; parent[i] = -1; } dist[start] = 0; parent[start] = 0; // 循环n次,每次加入一个点到树中 for (int i = 0; i < G.n; i++) { int u = -1; for (int j = 1; j <= G.n; j++) { if (!vis[j] && (u == -1 || dist[j] < dist[u])) { // 找到距离树最近的点 u = j; } } vis[u] = 1; // 更新其他点到树的距离 for (int v = 1; v <= G.n; v++) { if (!vis[v] && G.edges[u][v] < dist[v]) { dist[v] = G.edges[u][v]; parent[v] = u; } } } // 输出最小生成树的边 for (int i = 1; i <= G.n; i++) { if (parent[i] != -1) { printf("%d - %d: %d\n", parent[i], i, G.edges[i][parent[i]]); } } } ``` 4. 在主程序中调用相关算法输出最小生成树: ``` int main() { Graph G; createGraph(G); prim(G, 1); return 0; } ``` 对于上图,我们可以输入以下数据来测试算法: ``` 6 9 1 2 6 1 3 1 1 4 5 2 3 5 2 5 3 3 4 5 3 5 6 3 6 4 4 6 2 ``` 最后输出的结果为: ``` 1 - 3: 1 3 - 6: 4 2 - 5: 3 3 - 2: 5 3 - 4: 5 ``` 与Kruskal算法得到的结果一致。两种算法的共同点是它们都是贪心算法,都是从当前状态出发,每次选择当前最优的解,最终得到全局最优解。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值