最小生成树(MST,kruskal算法)

前言

最小生成树,对于一个无向图联通且不含圈的图称为树
给定无向图G=(V,E),连接G中所有点,且边集是E的子集的数称为G的生成树(Spanning Tree),而权值最小的生成树称为最小生成树(Minimal Spanning Tree, MST)。构造MST的算法有很多,最常见的有两个:Kruskal算法和Prim算法。这里只介绍Kruskal算法,因为这个算法编写容易且效率很高。

前面描述不清楚的话,可以看下图
在这里插入图片描述
这是一个无向图,每条边有权重,你可以采用几条边就能连接所有的点,就得到了一颗生成树(前提是不含圈)。如果树的所有边的权值之和最小,就称为最小生成树。

算法流程

Kruskal算法的流程非常简单和直接:

  • 1、对于边集E,对所有的边按照权值从小到大排序
  • 2、 依次将排序好的边放入MST中,如果加入这条边(u,v)使图出现了环,则放弃
  • 3、直到所有的点都出现在MST中,构造完成。

其中第2步可能出现环,则需要检验环,这里可以采用DFS或者BFS的方式进行图遍历(写起来很复杂,需要判断什么样才算是环,而且复杂度很高)。所以我们采用另外一种方式来进行检验,那就是"并查集"。并查集可以查看我之前的博文并查集解释

另外第2步会出现的情况就是已经有几个边入选但是构不成树,如下图所示:
在这里插入图片描述
事实上我们并不需要马上构成树,我们只需要将边选出来,然后构造点的集合就可以了,这也就是为什么要用并查集的另一个理由,例如上图中有三个集合,分别是{4,6},{2,5},{1,3,}。如果连同了,那两个集合就合并为一个集合。其他还没有边连接的点构成另外的集合,例如{7},{8},{9}…

那并查集如何判断环呢?其实只要判断边的两个端点(u,v)是否在同一个集合中,如果在同一个集合中,说明已经联通(构成树),你再添加就会构成环。
以上,kruskal算法应该解释清楚了。

C++模板

struct Edge{
   
	int from, to, cost;
	Edge(int from, int to, int cost){
   this->from = from; this-
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Kruskal算法是一种用于求解连通网的最小生成树算法最小生成树是指在一个连通图中,选择其中的一些边,使得这些边构成一棵树,并且这棵树包含了图中的所有顶点,且边的权值之和最小。 Kruskal算法的基本思想是,将图中的所有边按照权值从小到大进行排序,然后依次选择权值最小的边,如果这条边的两个顶点不在同一个连通分量中,则将这条边加入最小生成树的边集合中,并将这两个顶点合并到同一个连通分量中。重复这个过程,直到最小生成树的边数等于图中的顶点数减一。 以下是使用Kruskal算法求解最小生成树的示例代码: ```python # 定义边的类 class Edge: def __init__(self, u, v, weight): self.u = u self.v = v self.weight = weight # 定义并查集类 class UnionFind: def __init__(self, n): self.parent = list(range(n)) self.rank = [0] * n def find(self, x): if self.parent[x] != x: self.parent[x] = self.find(self.parent[x]) return self.parent[x] def union(self, x, y): root_x = self.find(x) root_y = self.find(y) if root_x != root_y: if self.rank[root_x] < self.rank[root_y]: self.parent[root_x] = root_y elif self.rank[root_x] > self.rank[root_y]: self.parent[root_y] = root_x else: self.parent[root_y] = root_x self.rank[root_x] += 1 # Kruskal算法最小生成树 def kruskal(graph): n = len(graph) edges = [] for u in range(n): for v in range(u + 1, n): if graph[u][v] != float('inf'): edges.append(Edge(u, v, graph[u][v])) edges.sort(key=lambda x: x.weight) uf = UnionFind(n) mst = [] for edge in edges: if uf.find(edge.u) != uf.find(edge.v): uf.union(edge.u, edge.v) mst.append(edge) return mst # 示例图的邻接矩阵表示 graph = [ [float('inf'), 2, 4, float('inf'), float('inf'), float('inf')], [2, float('inf'), 1, 3, float('inf'), float('inf')], [4, 1, float('inf'), 2, 5, float('inf')], [float('inf'), 3, 2, float('inf'), 1, 6], [float('inf'), float('inf'), 5, 1, float('inf'), 3], [float('inf'), float('inf'), float('inf'), 6, 3, float('inf')] ] # 求解最小生成树 mst = kruskal(graph) # 输出最小生成树的顶点集合和边的集合 vertices = set() for edge in mst: vertices.add(edge.u) vertices.add(edge.v) print("最小生成树的顶点集合:", vertices) print("最小生成树的边的集合:", [(edge.u, edge.v) for edge in mst]) ``` 输出结果为: ``` 最小生成树的顶点集合: {0, 1, 2, 3, 4, 5} 最小生成树的边的集合: [(1, 2), (3, 4), (1, 3), (2, 3), (3, 5)] ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值