最小生成树算法:Prim算法和Kruskal算法

简介

最小生成树处理的是无向图

常用算法:

  1. Prim算法 O(v²)适合稠密图
  2. Kruskal(克鲁斯卡尔)算法O(eloge)适合稀疏图

1.Prim算法

思想

贪心:将一点加入集合S,根据临边更新其他点到该集合的距离。找出最短距离,并将对应点加入集合S。重复以上步骤,加入集合S外的顶点,直至加入所有点或者发现非联通。

伪代码

    dist[i] <- 正无穷
    for (i=0; i < n; i++) // 迭代n次,加入n个点
        t <- 找到集合外距离最近的点 //s[t]为false,且dist[i]最小的点
        用 t 更新其他点到集合的距离
        s[t] = true

时间复杂度:O(v²)

代码

/*
S:当前已经在联通块中的所有点的集合
1. dist[i] = inf
2. for n 次
    t<-S外离S最近的点
    利用t更新S外点到S的距离
    st[t] = true
n次迭代之后所有点都已加入到S中
联系:Dijkstra算法是更新到起始点的距离,Prim是更新到集合S的距离
*/

const int N = 510, INF = 0x3f3f3f3f;

int n, m; // n个点,m条边
int g[N][N];// 邻接矩阵
int dist[N]; // 存储其他点到集合S的距离
bool st[N]; // 是否在集合内

int prim(){
    memset(dist, INF, sizeof dist);
    int res = 0;// 如果图不连通,返回INF,否则返回res

    for (int i = 0; i<n; i++){ // n次迭代,将n个点加入集合
        int t = -1; // 找到距离集合最近的点
        for (int j =1; j <= n; j++){
            if (!st[j] && (t == -1 || dist[t] > dist[j])){
                t = j;
            }
        }
        // 没有距离集合S小于INF的点
        if (i && dist[t] == INF) return INF; // 不连通

        if (i) res += dist[t];
        st[t] = true;

        // 更新到集合S的最短距离
        for (int j = 1; j <=n; j++) dist[j] = min(dist[j], g[t][j]);
    }
    return res;
}


2.Kruskal算法

思想

贪心:将图中的每个edge按照权重大小进行排序,每次从边集中取出权重最小且两个顶点都不在同一个集合的边加入生成树中!注意:如果这两个顶点都在同一集合内,说明已经通过其他边相连,因此如果将这个边添加到生成树中,那么就会形成环!重复直至所有边被处理过。

可以通过并查集实现。

伪代码

    将所有边按权重从小到大排序 O(mlogm)
    枚举每条边a <--> b,权重w
        if a,b不连通 (a、b不在同一个并查集内)
            将这条边加入集合中(合并a、b集合)

时间复杂度:O(elge)

代码

/*
res 最小生成树中的权重之和
cnt 当前加了多少条边
1.将所有边按权重排序O(mlogm)
2.枚举每条边(并查集应用) 
    if a,b 不连通
        加入集合
3.需重载< 
bool operator < (const Edge &C) const {
    return w < C.w;
}
*/
#include<bits/stdc++.h>
using namespace std;

const int N = 1e5+10, M = 2e5+10, INF = 0x3f3f3f3f;
int n, m; // n个点,m条边
int f[N]; // 并查集
struct Edge{
    int u, v, w;
    bool operator < (const Edge&e) const{
        return w < e.w; // 按权重从小到大排序
    }
}edges[M]; 

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

int kruskal(){
    sort(edges, edges+m);
    for (int i =0; i<= n; i++)f[i] = i; // 初始化并查集

    int res = 0, cnt = 0; // res:最小生成树权重之和;cnt:增加了多少条边
    for (int i = 0; i <m; i++){
        auto e = edges[i];
        int p = find(e.u), q = find(e.v);
        if (p != q){
            f[q] = p;
            res += e.w;
            cnt++;
        }
    }
    if (cnt < n-1) return INF;//集合内边数小于 n-1 ,不连通
    return res;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值