最小生成树(MST)

[复制链接]
发表于 2024-1-2 18:03:22 | 显示全部楼层 |阅读模式

已知 n 个城市,并且已知它们之间的距离。问怎样修路才能保证道路总长最短,并且每个城市都被连接?
(1) Prim算法  [邻接矩阵]
Prim算法是贪心算法,贪心策略为:找到目前情况下能连上的权值最小的边的另一端点,加入之,直到所有的顶点加入完毕。
Prim适用于稠密图。
朴素Prim的时间复杂度是O(n2),因为在寻找离生成树最近的未加入顶点时浪费了很多时间。所以,可以用堆进行优化。堆优化后的Prim算法的时间复杂度为O(mlogn)。
堆优化Prim的代码比较复杂,并查集优化的Kruskal算法与它相比,要好很多。
int minEdge[N], cloest[N];    // 与点N连接的最小边
int Prim(int start=0)     // start的出度不能为0!
{  int ans=0, k=0, min;
  
// 加入第一个点
for (int i=0;i<n;i++)
{
  minEdge=G[start];   cloest=start;
}
minEdge[start]=0;
  for (int i=0;i<n-1;i++)
{
  min=INF;    // 寻找离生成树最近的未加入顶点k
  for (int j=0;j<n;j++)
   if (minEdge[j]!=0 && minEdge[j]<min) min=minEdge[k=j];  
  // 把找到的边加入到MST中
  ans+=minEdge[k];   minEdge[k]=0;    // 加入完毕。以后不用再处理这个点。
  // 重新计算最短边
  for (int j=0;j<n;j++)    if (G[k][j]<minEdge[j])
   {
    minEdge[j]=G[k][j];     cloest[j]=k;
}
}
return ans;
}
(2) Kruskal算法!  [边目录]
Kruskal算法是贪心算法,贪心策略为:选目前情况下能连上的权值最小的边,若与以生成的树不够成环,加入之,直到n-1条边加入完毕。
时间复杂度为O(nlogm),最差情况为O(mlogn)。相比于Prim,这个算法更常用。
int parent[N], rank[M];           // p代表并查集,r是边的序号
int comp (const int i, const int j) {return w<w[j];} // 排序时使用
int find (int x)             // 带路径压缩的查找函数
{  return parent[x]==x ? x : parent[x] = find(parent[x]);
}  int Kruskal()
{  int ans = 0;  for (int i=0;i<n;i++) parent=i;  // 初始化并查集  for (int i=0;i<m;i++) rank=i;   // 边的序号(下面要按照边的权值大小来排序)  sort(rank, rank+m, comp);     // 按照边的权值大小排序
  for (int i=0;i<m;i++)
{
  int e=rank;   int x=find(u[e]), y=find(v[e]);  // 找出当前边的两个端点所在集合的编号
  if (x!=y)         // 如果不在同一集合,合并
  {
   ans += w[e];    parent[x] = y;
  }
}
return ans;
}

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表