【详解】最小生成树—无脑版

本文介绍了生成树的概念及其在图论中的应用,并详细讲解了两种构造最小生成树的算法——Prim算法和Kruskal算法,包括算法原理及其实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

生成树

定义:如果连通图G的一个子图是一棵包含G所有顶点的树
则称该子图为G的生成树

生成树是一棵树嘛,所以树上任两点之间有且只有一条通路。
这样就免去了环。

一个图的生成树可能会有很多,生成一棵树很简单
随便找一个顶点暴搜就可以
不同的顶点选择和不同的搜索方式都会导致最后的树不同

最小生成树

定义:连通图G的所有生成树中,边权之和最小的生成树就是图G的最小生成树

构造最小生成树最常用的方法有两种

一是Prim,二是Kruskal,如下

Prim

任意在图G中取一个点P
然后看 【以点P为一个端点的边 】中, 哪一个边权最小,把这个点与P点结合成一个集合

这个集合将会是我们的最小生成树
这时候我们的树里已经有两个点了

接着,我们要找到距离这个集合最近的一个点
也就是把这个集合当做一个整体,这个整体向外放射的所有边中有一个边的边权最小
那条边连接的点就是下一个要“收入囊中”的点

不断地重复这一步骤直到所有点都在集合里
我们的最小生成树就建完了
 
这种算法的时间复杂度为O(n^2)

下附代码(数组模拟邻接表)

void prim(){
int i, j, k;
int goal;
int mn;

memset(intree, 0, sizeof(intree));
memset(distt, INF, sizeof(distt));
distt[1] = 0;

for(i = 1; i <= n; i++){

   //以下为寻找到集合最近的点 
mn = INF;
goal = -1;
for(j = 1; j <= n; j++){
if(intree[j])continue;
if(distt[j] < mn){
mn = distt[j];
goal = j;
}
}
intree[goal] = 1;

//cout << mn << endl;
ans += mn;

if(goal == -1){
printf("orz\n");
return ;
}

//以下循环为点到集合距离的维护 
k = head[goal];
while(k != -1){
if(w[k] < distt[v[k]]){
distt[v[k]] = w[k];
}
k = next[k];
}
}
printf("%d\n", ans);
}


Kruskal

虽然比起这个算法,prim就略显繁琐了,但我还是更喜欢prim~

大致思路就是每次取边权最小的边,这些散的边共端点便合成一个一棵树
合成一棵树的时候会用到并查集了
最后聚合成的大树就是最小生成树

虽然不喜欢,但这个算法也是要写的
因为在图不是联通的, 要建多棵树的时候
kruskal比prim要方便一些(比如2013noip货车运输)

下附代码(洛谷3366板题):

int find(int x){
if(fa[x] == x)
   return x;
else
   return fa[x] = find(fa[x]);
}


bool cmp(node x, node y){
return x.wei < y.wei;
}


void init(){
int i, j;
int x, y, z;
    scanf("%d%d", &n, &m);
for(i = 1; i <= n; i++){
fa[i] = i;
}
    for(i = 1; i <= m; i++){
    scanf("%d%d%d", &x, &y, &z);
    cnt ++;
    edge[cnt].u = x;
edge[cnt].v = y;
edge[cnt].wei = z; 
    }
    sort(edge + 1, edge + 1 + cnt, cmp); 
}


void kruskal(){
int i, j;
int ans = 0;
for(i = 1; i <= m; i++){
if(find(edge[i].u ) != find(edge[i].v )){
fa[find(edge[i].u)] = edge[i].v;
ans += edge[i].wei;
}
}
int temp = find(1);
for(i = 2; i <= n; i++){
if(find(i) != temp){
printf("orz");
return ;
}
}
printf("%d", ans);
}


int main() {
init();
kruskal();
return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值