最小生成树——Prim算法(王道版)

参考王道《2023年数据结构考研复习指导》

#include <iostream>

#define MaxVerTexNum 20

typedef char VertexType;							// 顶点的数据类型
typedef int EdgeType;								// 带权图中边上权值的数据类型

// 邻接矩阵存储图
typedef struct {
	VertexType Vex[MaxVerTexNum];					// 顶点表
	EdgeType Edge[MaxVerTexNum][MaxVerTexNum];		// 邻接矩阵,边表(-1代表无穷大)
	int vexnum, arcnum;								// 图的当前顶点数和边数/弧数
} MGraph;

void MiniSpanTree_Prim(MGraph G, int u);
void Visit(const MGraph& G, int u, int v);

int main() {
	MGraph G = {
		{'1', '2', '3', '4', '5', '6'},
		{
			{-1, 6, 1, 5, -1, -1},
			{6, -1, 5, -1, 3, -1},
			{1, 5, -1, 5, 6, 4},
			{5, -1, 5, -1, -1, 2},
			{-1, 3, 6, -1, -1, 6},
			{-1, -1, 4, 2, 6, -1}
		},
		6, 10
	};

	MiniSpanTree_Prim(G, 0);

	system("pause");
	return 0;
}

// 从顶点u出发,构造最小生成树,并输出生成树的每条边
void MiniSpanTree_Prim(MGraph G, int u) {
	struct {
		int adjvex;							// 顶点编号
		int lowcost;						// 从顶点adjvex到当前顶点的权值(为0代表已加入集合U)
	}closedge[MaxVerTexNum];				// 求最小生成树时的辅助数组

	for (int i = 0; i < G.vexnum; ++i) {
		closedge[i].adjvex = u;
		closedge[i].lowcost = G.Edge[u][i];
	}

	closedge[u].lowcost = 0;				// 初始化,U = {u}

	for (int i = 1; i < G.vexnum; ++i) {	// 找n-1条边
		int min = -1;						// closedge数组中的最小权值
		int v;								// 下一个加入集合U的顶点

		for (int j = 0; j < G.vexnum; j++) {	// 找到closedge数组中的最小边
			if (min == -1 && closedge[j].lowcost > 0) {
				min = closedge[j].lowcost;
				v = j;
			}
			if (min != -1 && closedge[j].lowcost > 0 && closedge[j].lowcost < min) {
				min = closedge[j].lowcost;
				v = j;
			}
		}

		closedge[v].lowcost = 0;				// 将顶点v纳入集合U
		int u = closedge[v].adjvex;		
		Visit(G, u, v);

		for (int j = 0; j < G.vexnum; ++j) {	// 在顶点v并入U之后,更新closedge[i]
			if (closedge[j].lowcost != 0 && G.Edge[v][j] > 0) {
				if (closedge[j].lowcost == -1)
				{
					closedge[j].adjvex = v;
					closedge[j].lowcost = G.Edge[v][j];
				}
				else if (closedge[j].lowcost != -1 && G.Edge[v][j] < closedge[j].lowcost) {
					closedge[j].adjvex = v;
					closedge[j].lowcost = G.Edge[v][j];
				}
			}
		}
	}
}

void Visit(const MGraph& G, int u, int v) {
	std::cout << G.Vex[u] << " -> " << G.Vex[v] << " : " << G.Edge[u][v] << std::endl;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: Kruskal算法Prim算法都可以求解任何一个带权无向连通图的最小生成树。其中,Kruskal算法基于贪心思想,通过不断选择边权值最小且不会形成环的边来构建最小生成树;而Prim算法则是从一个起点开始,每次选择与当前生成树相邻且权值最小的边加入生成树中,直到生成树包含所有节点为止。两种算法的时间复杂度均为O(ElogE),其中E为边数。 ### 回答2: 任何一个带权无向连通图的最小生成树是指在该图中找到一棵包含所有节点的生成树,使得该生成树的边权之和最小。 为了更好地理解最小生成树,我们可以以一个具体的例子来说明。假设有如下一张无向图: ![image.png](https://cdn.luogu.com.cn/upload/pic/24255.png) 这张图中有6个节点和7条边。如果我们需要在该图中找到一棵包含所有节点的生成树,则可以得到如下几个解: - 选择边(1, 2)、(2, 3)、(2, 4)、(3, 5)、(4, 6),生成的树的边权之和为7+2+4+5+1=19 - 选择边(1, 2)、(2, 3)、(2, 4)、(4, 5)、(6, 4),生成的树的边权之和为7+2+4+3+1=17 - 选择边(1, 2)、(2, 4)、(3, 5)、(4, 5)、(4, 6),生成的树的边权之和为7+4+5+3+1=20 可以发现,虽然以上三个解都是包含所有节点的生成树,但其边权之和是不同的。其中,第二个解的边权之和最小,可以称其为该图的最小生成树。 从上述例子中可以看出,在寻找最小生成树时,我们需要在生成树中选择边的过程中,不断地计算边权之和,同时确保所生成的树包含图中所有的节点。这种算法中常用到的是Kruskal算法Prim算法。 Kruskal算法依据的是贪心策略,每次选择边权最小并且不与已选择的边构成环的边,依次将这些边加入生成树中。最终的生成树即为最小生成树Prim算法也是一种贪心算法,其选择边的方式与Kruskal算法不同。Prim算法从任一节点出发,每次将与当前生成树距离最短的未选择的节点连接起来,逐步扩大生成树的范围,直到所有节点都被包含在生成树中。最终的生成树即为最小生成树。 总之,最小生成树是一个经典的图论问题,在实际应用中具有广泛的价值和意义。了解并掌握相应的算法,可以有效地解决实际问题,提高数据处理的效率。 ### 回答3: 最小生成树,也被称为MST(Minimum Spanning Tree),是指在一张带权图中,将所有节点彼此连接起来且总权值最小的树。在实际应用中,最小生成树可以帮助我们寻找最优的物流路径、路网系统等问题。 任何一个带权无向连通图的最小生成树,可以使用Prim算法或Kruskal算法来计算。这两种算法都是贪心算法,用来选择权值最小的边来构建最小生成树Prim算法基于节点,从一个固定的起点开始构建最小生成树,每次在当前生成树中找到最近的未加入节点,然后加入这个节点到当前树中去。Prim算法通过建立一个优先队列,不断地选取权值最小的边来构建最小生成树。 Kruskal算法基于边,将所有边按照权值从小到大排序,每次选择一条没有形成环的边加入生成树中。如果新加入一条边会形成环,则不加入这条边,并选择一条权值更小的边。Kruskal算法通过并查集来判断是否产生环,并在遍历完所有边之后得到最小生成树。 需要注意的是,如果带权图不是连通图,那么最小生成树就不存在。如果要处理非连通图,可以先把图进行连通分量的划分,然后再对每个连通分量分别求最小生成树。 总之,无论是Prim算法还是Kruskal算法,对于任何一个带权无向连通图,都可以用贪心算法来求出最小生成树。这样的最小生成树可以帮助我们寻找最优的路径,优化网络,使得节点之间连接更加紧密,大大提高了系统的可靠性和效率。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈阿土i

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值