最小生成树(Minimum Spanning Tree MST)是在一个给定的无向图G(V,E)中求一棵树T,使得这棵树拥有图G中的所有顶点,且所有边都是来自图G中的边,并且满足整棵树的边权和最小。
最小生成树的3个性质:
1、最小生成树是树,因此其边数等于顶点数减1,且树内一定不会有环。
2、对给定的图G(V,E),其最小生成树可以不唯一,但其边权和一定是唯一的。
3、由于最小生成树是在无向图上生成的,因此其根节点可以是这棵树上的任意一个结点。
求解最小生成树一般有两种算法,即prim算法和kruskal算法。这两个算法都采用了贪心的思想,只是贪心的策略不太一样。
prim算法
prim算法和Dijkstra算法十分相似,主要区别是d[]数组的含义不同。在Dijkstra算法中d[v]数组是结点v到达源点s的最短距离,在prim算法中d[v]数组是结点v到达集合S的最短距离。同样有邻接矩阵和邻接表版本。
如果有同学忘记了Dijkstra算法,可以看博主的上上篇关于最短路径的文章,链接参考如下👇
## 输入样例
6 10 0
0 1 4
0 4 1
0 5 2
1 2 6
1 5 3
2 3 6
2 5 5
3 4 4
3 5 5
4 5 3
def prim(s):
global d,vis,ans
d[0]=0
for i in range(n):
u=-1
MIN=float("inf")
for j in range(n):
if vis[j]==False and d[j]<MIN:
u=j
MIN=d[j]
if u==-1:
return
vis[u]=True
ans+=d[u]
for v in range(n):
if vis[v]==False and G[u][v]!=float("inf"):
if G[u][v]<d[v]:
d[v]=G[u][v]
return ans
n,m,s=map(int,input().strip().split())
d=[float("inf")]*n
vis=[False]*n
ans=0
G=[[float("inf")]*n for _ in range(n)]
for _ in range(m):
u,v,w=map(int,input().strip().split())
## 无向图
G[u][v]=G[v][u]=w
print(prim(s))
## 输出样例
15
kruskal算法同样是解决最小生成树问题的一个算法。和prim算法不同,kruskal算法采用了边贪心的策略,其思想极其简洁,理解难度比prim算法要低很多。
kruskal算法的基本思想为:在初始时隐去图中的所有边,这样图中每个顶点都自成一个联通块。然后执行以下操作:
1、对所有边按边权从小到大进行排序
2、按边权从小到大测试所有边,如果当前测试边所连接的顶点不在一个联通块,则把这条测试边加入当前最小生成树中,否则舍弃。
3、执行步骤2,直至最小生成树中的边数等于顶点数减1或是测试完所有边时结束。而当结束时如果最小生成树的边数小于顶点数减1,说明该图不连通。
## 输入样例
6 10 0
0 1 4
0 4 1
0 5 2
1 2 6
1 5 3
2 3 6
2 5 5
3 4 4
3 5 5
4 5 3
class edge:
def __init__(self,u=None,v=None,w=None):
self.u=u
self.v=v
self.w=w
## 并查集查询函数
def findFather(x):
# a=x
while (x!=father[x]):
x=father[x]
# while (a!=father[a]):
# z=a
# a=father[a]
# father[z]=x
return x
def kruskal(n,m):
global father
ans=0
edge_num=0
## 顶点范围0-n-1
for i in range(n):
## 并查集初始化
father[i]=i
## 所有边按边权从小到大排列
E.sort(key=lambda x:x.w)
## 枚举所有边
for i in range(m):
faU=findFather(E[i].u)
faV=findFather(E[i].v)
## 如果不在一个集合中
if faU!=faV:
## 合并集合
father[faU]=faV
ans+=E[i].w
edge_num+=1
if edge_num==n-1:
break
if edge_num!=n-1:
return -1
else:
return ans
n,m=map(int,input().strip().split())
E=[[] for _ in range(m)]
for i in range(m):
E[i]=edge()
u,v,w=map(int,input().strip().split())
E[i].u=u
E[i].v=v
E[i].w=w
father=[-1]*n
print(kruskal(n,m))
## 输出样例
15