004.最小生成树算法

最小生成树(MST)

Prim算法

\(e[u]\)\(u\)点的所有邻边的终点和边权

\(d[u]\)\(u\)点与圈外邻点的最小距离,\(vis[u]\)标记\(u\)点是否出圈

基础版本

算法流程类似于Dijkstra算法,不断选距离最小的点出圈,直到圈内为空

  1. 初始化,所有点都在圈(集合)内,即\(vis=0\)\(d[s] = 0\),其他点\(d[i]=\inf(i \ne s)\)
  2. 每次从圈内选取一个距离最小的点\(u\),打标记出圈
  3. \(u\)的所有邻点的距离执行更新操作
  4. 重复2,3步操作,直到圈内为空
struct edge{int v,w;};
vector<edge> e[N];
int d[N],vis[N];

bool prim(int s){
    for(int i = 0 ; i <= n ; i ++) d[i] = inf;
    d[s] = 0;
    for(int i = 1 ; i <= n ; i ++){
        int u = 0;
        for(int j = 1 ; j <= n ; j ++)
            if(!vis[j] && d[j] < d[u]) u = j;
        vis[u] = 1;  //标记u已经出圈
        ans += d[u];
        if(d[u] != inf) cnt ++;
        for(auto ed : e[u]){
            int v = ed.v,w = ed.w;
            if(d[v] > w) d[v] = w;
        }
    }
    return cnt == n;
}

时间复杂度\(O(n^2)\)

堆优化版

创建一个pair类型的大根堆\(q\{-d[u],u\}\),把距离其负值,距离最小的元素最大,一定在堆顶

  1. 初始化\(\{0,s\}\)入队,\(d[s] = 0\),其他点\(d[i] = \inf(i\ne s)\)
  2. 从队头弹出距离最小的点\(u\),若\(u\)扩展过则跳过,否则打标记
  3. \(u\)的所有邻点的距离执行更新操作,把\(\{-d[v],v\}\)压入队
  4. 重复2,3步操作,直到队列为空
int n,m,s,a,b,c,ans,cnt;
struct edge{int v,w;};
vector<edge> e[N];
int d[N],vis[N];
priority_queue<pair<int,int>> q;

bool prim(int s){
    for(int i = 0 ; i <= n ; i ++) d[i] = inf;
    d[s] = 0; q.push({0,s});
    while(q.size()){
        int u = q.top().second; q.pop();
        if(vis[u]) continue;
        vis[u] = 1; //标记u出队
        ans += d[u]; cnt ++;
        for(auto ed : e[u]){
            int v = ed.v,w = ed.w;
            if(d[v] > w){
                d[v] = w;
                q.push({-d[v],v}); //大根堆
            }
        }
    }
    return cnt == n;
}

时间复杂度\(O(m\log m)\)

Kruskal算法

利用并查集求最小生成树

\(e[i]\)存第\(i\)条边的起点、终点与边权

\(fa[x]\)\(x\)点的父节点

算法流程

  1. 初始化并查集,把\(n\)个点放在\(n\)个独立的集合
  2. 将所有的边按边权从小到大排序(贪心思想)
  3. 按顺序枚举每一条边,如果这条边链接的两个点不在同一集合,就把这条边加入最小生成树,并且合并这两个集合;如果这条边链接的两个点在同一个集合,就跳过
  4. 重复执行3,直到选取了\(n-1\)条边为止
struct edge{
    int u,v,w;
    bool operator < (const edge& t) const
    {
        return w < t.w;
    }
}e[N];
int fa[N],ans,cnt;

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

bool Kruskal(){
    sort(e,e+m);
    for(int i = 1 ; i <= n ; i ++) fa[i] = i;
    for(int i = 0 ; i < m ; i ++){
        int x = find(e[i].u);
        int y = find(e[i].v);
        if(x != y){
            fa[x] = y;
            ans += e[i].w;
            cnt ++;
        }
    }
    return cnt == n-1;
}
posted @ 2025-06-16 20:09  _P_D_X  阅读(21)  评论(0)    收藏  举报