MST(最小生成树)
一、什么是图的最小生成树(MST)
-
N个点用N-1条边连接成一个连通块,形成的图形只可能是树,没有别的可能。
-
一个有N个点的图,边一定是大于等于N-1条的。图的最小生成树,就是在这些边中选择N-1条出来,连接所有的N个点。这N-1条边的边权之和是所有方案中最小的。
二、最小生成树用来解决什么问题?
就是用来解决如何用最小的“代价”用N-1条边连接N个点的问题。
三、Prim算法
-
Prim算法采用与Dijkstra、Bellman-Ford算法一样的“蓝白点”思想:白点代表已经进入最小生成树的点,蓝点代表未进入最小生成树的点。
算法描述:(时间复杂度n的2次方(邻接矩阵),邻接表O(E log(n))其中E为边数)
以1为起点生成最小生成树,min[v]表示蓝点v与白点相连的最小边权,MST表示最小生成树的权值之和。
伪代码
a)初始化:min[v]= ∞(v≠1); min[1]=0;MST=0;
b)for (i = 1; i<= n; i++)
1.寻找min[u]最小的蓝点u。
2.将u标记为白点
3.MST+=min[u]
4.for 与白点u相连的所有蓝点v
if (w[u][v]<min[v])
min[v]=w[u][v];
c)算法结束: MST即为最小生成树的权值之和
堆优化code
int Prim(int st){
priority_queue<node>q;
for(int i=1;i<=n;++i)d[i]=0x3f3f3f3f;
q.push((node){st,0});
d[st]=0;
int ans=0;
while(!q.empty()){
node x=q.top();
q.pop();int id=x.id;
if(!vis[id]){
vis[id]=1;
ans+=d[id];
for(int i=head[id];i;i=e[i].next){
int v=e[i].to;
d[v]=min(d[v],e[i].dis);
if(!vis[v]){
q.push((node){v,d[v]});
}
}
}
}
return ans;
}
-
注意,这里与dijistra的区别。dijistra中min[v]表示v与起点的距离,这里表示与最近的蓝点的距离
简单证明
反证法
假设权值最小的边不在最小生成树中。
此时将权值最小的边加入生成树中,那么必然会构成一 个回路(最小生成树有n个点,最小边加入之后会有n条边),任意去掉环里比权值最小的边权值大的一条边,这样就构造了更优的一个解,这时与假设构成矛盾。所以权值最小的边一定在最小生成树中。
四、Kruskal
- Kruskal算法每次从剩下的边中选择一条不会产生环路的具有最小耗费的边加入已选择的边的集合中。注意到所选取的边若产生环路则不可能形成一棵生成树。
Kruskal算法分e 步,其中e 是网络中边的数目。按耗费递增的顺序来考虑这e 条边,每次考虑一条边。当考虑某条边时,若将其加入到已选边的集合中会出现环路,则将其抛弃,否则,将它选入。
用并查集维护在同一棵树上的点,如果边上的两点已经在一棵树上,加边后一定会有环。
Kruskal算法在连边的时候实际上是把两个连通块相连,如果图本身连通则运行完之后应该只有一个连通块
int liantongkuai=n;//一个点就是一个连通块
int Kruscal(){
for(int i=1;i<=n;++i)f[i]=i;
sort(e+1,e+k+1,cmp);
int m=0,ans=0;
for(int i=1;i<=k;++i){
if(m==n-1)break;
int x=e[i].from,y=e[i].to;
if(find(x)!=find(y)){
ans+=e[i].dis;m++;Union(x,y);liantongkuai--;
}
}
return ans;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库