最小生成树算法

Prim算法

P r i m Prim Prim算法和 D i j k s t r a Dijkstra Dijkstra最短路算法十分相似。我们从 1 1 1号节点出发,不断地向生成树里加最小权值边。

假设我们已有一颗树 T T T,满足 T T T为一颗最小生成树的子图。令 X X X T T T的点集, e e e为连接 X X X V ∖ X V\setminus X VX的最小权值边,我们要证明存在一棵生成树包含了 T T T e e e

同样,假设包含 T T T的最小生成树不包含 e e e,那么将 e e e加到这颗树里之后一定会形成一个环,且此时除 e e e以外还有另一条边 f f f连接着 X X X V ∖ X V\setminus X VX。根据定义 w f ≥ w e w_f \ge w_e wfwe,所以我们将 f f f从树中删去,然后加上 e e e就可以得到一颗新的生成树且权值和不超过原来的生成树。

所以我们不断地加入最小权值边,直到 X = V X=V X=V,由于存在一颗生成树满足 T T T是它的子图,于是就求得了一颗最小生成树 T T T

时间复杂度: O ( ∣ E ∣ log ⁡ ∣ V ∣ ) O(|E|\log|V|) O(ElogV)

空间复杂度: O ( ∣ V ∣ + ∣ E ∣ ) O(|V| + |E|) O(V+E)

代码
int n, m;
vector<Edge> e[N];

LL d[N];
bool used[N];
LL Prim(){                                  // 默认图联通
    rep1(i, n + 1) d[i] = inf;
    d[1] = 0;
    priority_queue<pair<LL, int> > pq;
    pq.push(MP(-d[1], 1));
    used[1] = true;
    LL ret = 0;
    while (!pq.empty()){
        int cur = pq.top().second;
        LL curdis = -pq.top().first;
        pq.pop();
        if (curdis > d[cur]) continue;
        ret += curdis;
        used[cur] = true;
        for (Edge u : e[cur]) if (u.len < d[u.to] && !used[u.to]){
            d[u.to] = u.len;
            pq.push(MP(-d[u.to], u.to));
        }
    }
    return ret;
}

Kruskal算法

K r u s k a l Kruskal Kruskal是一个非常简单实用的最小生成树算法,代码难度比 P r i m Prim Prim低很多,而且跑得和 P r i m Prim Prim一样快,一般情况下还有推荐使用 K r u s k a l Kruskal Kruskal

K r u s k a l Kruskal Kruskal算法的核心思想就是将所有边按权值从小到大排序,不断地判断当前边加入后会不会使生成树出现环,如果不会形成环,那么就将当前边加入生成树。判环操作其实就等价于判断当前边的两个端点是否已属于同一集合,这可以用并查集很方便地维护。

时间复杂度: O ( ∣ E ∣ log ⁡ ∣ V ∣ ) O(|E|\log|V|) O(ElogV)

空间复杂度: O ( ∣ E ∣ ) O(|E|) O(E)

代码
int n, m;
vector<Edge> edges;

LL Kruskal(){      // 默认图联通
    sort(edges.begin(), edges.end(), [&](Edge x, Edge y){ return x.len < y.len; });
    dsu.init();
    int cnt = 0;
    LL ret = 0;
    rep(i, edges.size()){
        if (cnt == n - 1) break;
        if (dsu.find(edges[i].from) == dsu.find(edges[i].to)) continue;
        cnt ++;
        ret += edges[i].len;
        dsu.merge(edges[i].from, edges[i].to);
    }
    return ret;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值