最小生成树的两种算法

前言:

  • 生成树:在一个图中将所有的结点连在一起建成一颗树(最小连通图

  • 最小生成树:权值最小的生成树

因此,如果是生成树,那么原图必定是一个连通图

Prim算法

  • 时间复杂度 O(|V|2),适和稠密图

基本过程

  1. 将图分成两个部分,在树里的部分和不在树里的部分

  2. 选择一条权值最小的边,且该边所连的两个点一个在树内,一个在树外

  3. 将选定的边加入树内,并将该边的权值记录

  4. 重复上述操作直到所有的点都被加入树中

实现

目标

读入一个含有 n 个点的带权无向图,计算最小生成树

代码

对于判断选定边所连的两个点是否一个在树内,一个在树外,可以用 vis 数组来判断
而选择权值最小的边,则借用优先队列实现

#include <bits/stdc++.h>
using namespace std;
#define M 500000
#define N 100000
int n, m, res = 0;
//链式前向星建图
struct Node{
    int x, v, next;
}edge[M*2+1];
int head[N+1], cnt = 0, dis[N+1];
void addedge(int x, int y, int z){
    edge[++cnt].x = y;
    edge[cnt].v = z;
    edge[cnt].next = head[x];
    head[x] = cnt;
}
//边权优先队列
struct ty{
    int x, len;  
    //比大小重载
    bool operator < (const ty &a) const{
        return len > a.len;
    }
}temp;
priority_queue<ty>q;
bool vis[N+1];

void prim()
{
    //以初始点1更新队列
    vis[1] = true;
    for(int i = head[1]; i != -1; i = edge[i].next){
        temp.x = edge[i].x;
        dis[edge[i].x] = edge[i].v;
        temp.len = edge[i].v;
        q.push(temp);
    }
    while(!q.empty()){
        //将当前边权最小的可连接点加入树中
        //并更新最小生成树代价
        ty tmp = q.top(); q.pop();
        if(vis[tmp.x]) continue;
        vis[tmp.x] = true;
        res += tmp.len;
        //将新加入点所连接的边放进队列
        for(int i = head[tmp.x]; i != -1; i = edge[i].next){
            if(vis[edge[i].x]) continue;
            //大于等于之前所选边权的新边权无需放入
            if(dis[edge[i].x] <= edge[i].v) continue; 
            //
            tmp.x = edge[i].x;
            dis[edge[i].x] = edge[i].v;
            tmp.len = edge[i].v;
            q.push(tmp);
        }
    }
    //输出
    cout <<res;
}

int main(void)
{
    memset(head, -1, sizeof(head));
    memset(dis, 0x3f, sizeof(dis));
    memset(vis, false, sizeof(vis));
    
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= m; i++){
        int a, b, v;
        scanf("%d%d%d", &a, &b, &v);
        addedge(a, b, v);
        addedge(b, a, v);
    }
    prim();
    return 0;
}

Kruskal算法

  • 它与prim算法同样都是利用了贪心的策略,只是krusal算法并不会利用树内和树外的分类来选边,而是利用并查集
  • 时间复杂度 O(e log2e),适和稀疏图

基本过程

  1. 将所有的边按大小进行排序(优先队列

  2. 选择一条之前 未选择过的 且 权值最小的 边

  3. 利用并查集判断选出的边所连接的两个点当前是否在一个集合里,如果不在,就将该边加入最小生成树,并更新并查集信息

  4. 重复2、3操作直到所有的结点生成一颗树

实现

目标

读入一个含有 n 个点的带权无向图,计算最小生成树

代码
#include <bits/stdc++.h>
using namespace std;
#define M 500000
#define N 100000
int n, m, res = 0;
//边结构体
struct Node{
    int x, y, v;
}edge[M+1];
//按边权值升序排列
bool cmp(Node a, Node b){
    return a.v < b.v;
}
//并查集查找父节点以及路径压缩
int fa[N+1];
int find(int x){
    if(fa[x] == x)
        return x;
    else
        return fa[x] = find(fa[x]);
}

void Kruskal()
{
    //初始化并查集
    for(int i = 1; i <= n; i++) fa[i] = i;
    //按边权排序
    sort(edge+1, edge+1+m, cmp);
    //依次拿边
    for(int i = 1; i <= m; i++){
        //判断选定边所连的两个点是否已经在树里
        int fx = find(edge[i].x);
        int fy = find(edge[i].y);
        if(fx == fy) continue;
        res += edge[i].v;
        fa[fx] = fy;
    }
    cout <<res;
}
int main(void)
{
    scanf("%d%d", &n, &m); 
    for(int i = 1; i <= m; i++){
        int a, b, v;
        scanf("%d%d%d", &edge[i].x, &edge[i].y, &edge[i].v);
    }
    Kruskal();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

UmVfX1BvaW50

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

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

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

打赏作者

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

抵扣说明:

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

余额充值