Prim算法(稠密图)
算法步骤
1. 将1号节点加入最小生成树
2. 寻找不在生成树中的顶点到生成树的最短边 // O(n) ,可用小根堆优化为O(log m)
3. 将其加入最小生成树
4. 更新所有节点到最小生成树的距离
5. 重复2-4操作,直到循环n次,或者第二步得到的最短边为INF时 结束循环。
C++代码如下
const int N=510,INF=0x3f3f3f3f;
int g[N][N],dist[N];
int n,m;
bool st[N]; // 是否在最小生成树中
int prim(){
memset(dist,0x3f,sizeof dist);
int ans=0;
for(int i=0;i<n;i++){ // 循环n次
int t=-1;
for(int j=1;j<=n;j++){ // 寻找不在生成树中的顶点到生成树的最短边
if(!st[j] && (t==-1||dist[t]>dist[j]))t=j;
}
if(i&& dist[t]==INF)return INF; // 第二步得到的最短边为INF时 结束循环。
st[t]=true; // 将其加入最小生成树
if(i)ans+=dist[t]; // 若不是第一点,则计算其长度
for(int j=1;j<=n;j++){ // 更新到最小数的最短距离,有的可能最INF
dist[j]=min(dist[j],g[t][j]); // 这里不用dist[t]+g[t][j],因为不是从起点出发
}
}
return ans;
}
算法评价
Prim算法使用于稠密图,时间复杂度为O(n^2)
,可用小根堆优化为O(nlogm)
Kruskal算法
算法步骤
1. 建立并查集,并为每个点建立一个集合 // `O(n)`
2. 将所有边按权重从小到大排序 //`O(mlogm)`
3. 依次扫描每个边(x,y,w) // `O(m)`
若x,y属于同一集合(连通),则忽略这条边,扫描下一条
否则,合并x,y所在的集合,将w加到答案中
4. 所有边扫描完,则构成了最小生成树
5. 若加入最小生成树的边小于n,则代表给出的图不是连通图
C++代码
const int N=1e5+10,M=2e5+10,INF=0x3f3f3f3f;
struct Edge{
int a,b,w;
}edges[M];
int n,m;
int ans=0;
int cnt=0;
int p[N]; // 并查集
int find(int x){
if(x!=p[x])p[x]=find(p[x]);
return p[x];
}
int cmp(Edge a,Edge b){
return a.w<b.w;
}
void kruskal(){
for(int i=1;i<=n;i++)p[i]=i; // 初始化并查集 并赋值
sort(edges+1,edges+m+1,cmp); // 对所有边排序(下标从1开始存)
for(int i=1;i<=m;i++){ // 扫描每条边
int a=edges[i].a,b=edges[i].b,w=edges[i].w;
a=find(a),b=find(b);
if(a==b){ // 若两个顶点属于一个集合,则扫描下一个集合
continue;
}
else { // 若两个顶点不属于同一个集合,则将w加入到答案中
p[a]=b;
ans+=w;
cnt++;
}
}
if(cnt<n-1)ans=INF;
}
算法评价
很显然,算法的时间复杂度为O(n)
或者O(mlogm)
,一般用Kruskal存稀疏图,m<n2 所以最终的时间复杂度为O(mlogm)
,