最小生成树(Kruska、Prim)

本文详细介绍了两种经典的最小生成树算法——Kruskal算法和Prim算法。Kruskal算法通过并查集避免形成环路,适用于稀疏图;Prim算法从任意顶点开始逐步扩展,适用于密集图。文中提供了详细的实现代码,帮助读者理解算法原理。

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

Kruska:
将所有边从小到大加入,在此过程中 判断是否构成回路 
– 使用数据结构:并查集 
– 时间复杂度:O(ElogE) 
– 适用于稀疏

堆(结构体优先队列)

http://acm.hdu.edu.cn/showproblem.php?pid=1233

#include<bits/stdc++.h>
using namespace std;
const int N=1e3;
struct node
{
    int x,y,val;
    node(){}
    node(int a,int b,int c){x=a;y=b;val=c;}
    bool operator <(const node &xx)const
    {
        return xx.val<val;
    }
};
int pre[N];
int finds(int x)
{
    return x==pre[x]?x:pre[x]=finds(pre[x]);
}
bool bing(int x,int y)
{
    x=finds(x);
    y=finds(y);
    if(x!=y) {pre[x]=y;return 1;}
    return 0;
}
int main()
{
    int i,j,t,tt=0;
    int n,m;
    while(~scanf("%d",&n),n)
    {
        for(i=0;i<=n;i++) pre[i]=i;
        m=(n-1)*n/2;
        priority_queue<node> q;
        while(!q.empty()) q.pop();
        while(m--)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            q.push(node(a,b,c));
        }
        int ans=0;
        while(!q.empty())
        {
            node g=q.top();q.pop();
            if(bing(g.x,g.y)) ans+=g.val;
        }
        printf("%d\n",ans);
    }
}
sort(快排)

#include<bits/stdc++.h>
using namespace std;
const int N=1e2;
struct node
{
    int x,y,val;
    node(){}
    node(int a,int b,int c){x=a;y=b;val=c;}
}g[5000];
bool cmp(node xx,node yy)
{
    return xx.val<yy.val;
}
int pre[N];
int finds(int x)
{
    return x==pre[x]?x:pre[x]=finds(pre[x]);
}
bool bing(int x,int y)
{
    x=finds(x);
    y=finds(y);
    if(x!=y) {pre[x]=y;return 1;}
    return 0;
}
int main()
{
    int i,j,t,tt=0;
    int n,m;
    while(~scanf("%d",&n),n)
    {
        for(i=0;i<=n;i++) pre[i]=i;
        m=(n-1)*n/2;
        for(i=0;i<m;i++)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            g[i]=(node(a,b,c));
        }
        sort(g,g+m,cmp);
        int ans=0;n--;
        for(i=0;i<m,n;i++)
            if(bing(g[i].x,g[i].y)) ans+=g[i].val,n--;
        printf("%d\n",ans);
    }
}

Prim:
从任一节点出发,不断扩展 
– 使用数据结构:堆 
– 时间复杂度:O(ElogV) 或 O(VlogV+E)(斐波那契堆) 
– 适用于密集图 
– 若不用堆则时间复杂度为O(V2) 


未优化

#include<bits/stdc++.h>
using namespace std;
const int N=1e2+7;
const int inf=0x3f3f3f3f;
int mp[N][N],dis[N],used[N];
int n;
void prim()
{
    int i,j,pos,mi,sum=0;
    memset(used,0,sizeof used);
    memset(dis,inf,sizeof dis);
    pos=1;used[1]=1;dis[1]=0;
    for(i=2;i<=n;i++)
    {
        for(j=1;j<=n;j++)
            if(!used[j]&&dis[j]>mp[pos][j])
            dis[j]=mp[pos][j];
        mi=inf;
        for(j=1;j<=n;j++)
            if(!used[j]&&dis[j]<mi)
            mi=dis[pos=j];
        used[pos]=1;
        sum+=mi;
    }
    cout<<sum<<endl;
}
int main()
{
    int i,j,t,tt=0;
    while(cin>>n,n)
    {
        memset(mp,0,sizeof mp);
        for(i=1;i<=n*(n-1)/2;i++)
        {
            int a,b,c;
            cin>>a>>b>>c;
            mp[a][b]=mp[b][a]=c;
        }
        prim();
    }
    return 0;
}

#include<bits/stdc++.h>
using namespace std;
const int N=2*1e2+7;
const int inf=0x3f3f3f3f;
struct node{
    int x,val;
    node(){}
    node(int a,int b){x=a;val=b;}
    bool operator <(const node &xx)const
    {
        return val>xx.val;
    }
};
vector<node> g[N];
int used[N],dis[N],n;
void prim()
{
    int sum=0;
    priority_queue<node> q;
    memset(used,0,sizeof used);
    memset(dis,inf,sizeof dis);
    q.push(node(1,0));
    while(!q.empty())
    {
        node now=q.top();q.pop();
        int x=now.x;
        if(used[x]) continue;
        used[x]=1;
        sum+=now.val;
        for(int i=0;i<g[x].size();i++)
        {
            int xx=g[x][i].x;
            int val=g[x][i].val;
            if(!used[xx]&&dis[xx]>val)
                q.push(node(xx,dis[xx]=val));
        }

    }
    cout<<sum<<endl;
}
int main()
{
    int i,j,t,tt=0;
    while(cin>>n,n)
    {
        memset(g,0,sizeof g);
        for(i=0;i<n*(n-1)/2;i++)
        {
            int a,b,c;
            cin>>a>>b>>c;
            g[a].push_back(node(b,c));
            g[b].push_back(node(a,c));
        }
        prim();
    }
    return 0;
}


#include<bits/stdc++.h>
using namespace std;
const int N=2*1e2+7;
const int inf=0x3f3f3f3f;
struct node{
    int to,val;
    node(){}
    node(int a,int b){to=a;val=b;}
    bool operator <(const node &xx)const
    {
        return val>xx.val;
    }
};
int mp[N][N];
int used[N],dis[N],n;
void prim()
{
    int sum=0;
    priority_queue<node> q;
    memset(used,0,sizeof used);
    memset(dis,inf,sizeof dis);
    q.push(node(1,0));
    while(!q.empty())
    {
        node now=q.top();q.pop();
        int from=now.to;
        if(used[from]) continue; used[from]=1;

        sum+=now.val;
        for(int i=1;i<=n;i++)
        {
            int val=mp[from][i];
            if(inf==val) continue;
            if(!used[i]&&dis[i]>val)
                q.push(node( i,dis[i]=val ));
        }
    }
    cout<<sum<<endl;
}
int main()
{
    int i,j,t,tt=0;
    while(cin>>n,n)
    {
        memset(mp,inf,sizeof mp);
        for(i=0;i<n*(n-1)/2;i++)
        {
            int a,b,c;
            cin>>a>>b>>c;
            mp[a][b]=mp[b][a]=c;
        }
        prim();
    }
    return 0;
}


### Python 实现最小生成树算法:Kruskal 和 Prim #### 1. Kruskal 算法 Kruskal 算法是一种基于贪心策略的算法,其主要思想是通过不断选取权重最小的边来构建最小生成树 (Minimum Spanning Tree, MST),同时确保不会形成环路。以下是其实现过程: ```python class UnionFind: def __init__(self, n): self.parent = list(range(n)) def find(self, u): while u != self.parent[u]: self.parent[u] = self.parent[self.parent[u]] # 路径压缩 u = self.parent[u] return u def union(self, u, v): root_u = self.find(u) root_v = self.find(v) if root_u != root_v: self.parent[root_u] = root_v def kruskal(edges, num_nodes): edges.sort(key=lambda edge: edge[2]) # 按照权重升序排列 uf = UnionFind(num_nodes) mst = [] total_weight = 0 for u, v, w in edges: if uf.find(u) != uf.find(v): # 如果两个节点不属于同一集合,则连接它们 uf.union(u, v) mst.append((u, v, w)) total_weight += w return mst, total_weight ``` 上述代码实现了 Kruska 算法的核心逻辑[^4]。`Union-Find` 数据结构被用来检测是否存在环路。 --- #### 2. Prim 算法 Prim 算法同样采用贪心策略,它从任意一个顶点出发逐步扩展已有的部分生成树,直到覆盖所有顶点为止。下面是该算法的一个具体实现方式: ```python import heapq def prim(graph, start_node=0): visited = set() heap = [(0, start_node)] # 初始堆为(权值, 节点) mst = [] # 存储最终的MST total_weight = 0 while heap and len(visited) < len(graph): weight, node = heapq.heappop(heap) if node not in visited: visited.add(node) total_weight += weight if weight > 0: # 排除初始加入的情况 mst.append((prev_node, node, weight)) for neighbor, cost in graph[node]: # 将邻居节点加入优先队列 if neighbor not in visited: heapq.heappush(heap, (cost, neighbor)) prev_node = node return mst, total_weight ``` 这段代码展示了如何利用优先队列(最小堆)动态选择当前未访问过的最短路径[^5]。 --- #### 总结 两种方法都能有效解决问题,但在实际应用中有一定差异: - **时间复杂度**: 对于稀疏图而言,Kruskal 的效率较高;而对于稠密图来说,Prim 更加适合。 - **空间需求**: Prim 使用邻接表表示更节省内存资源。 以上就是关于 Python 中实现最小生成树算法的具体说明以及对应的代码示例[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值