求最小生成树两种方法总结(kruskal与prim)

Kruskal:

算法思想:贪心,从边出发。要找出最小生成树,可以每一步都找一个边权尽量小的边,并且这个边连接的两个点没有被已经选的边链接(直接链接,或者间接链接)。这样每一步都找的是符合当前情况的最小边,那么结果自然也是最小的。
复杂度:O(ElogE) 适合稀疏图
步骤:

  1. 将边按照边权快速排序
  2. 从边权小的向边权大的遍历边,判断这个边链接的两个点是否已经在同一集合中(使用并查集),如果这两个点已经在一个集合中了,说明这条边之前已经有边权更小的边去链接这两个点了。就跳过这条边。
  3. 每次由步骤2得到边的时候判断得到边的数量,如果等于n-1,说明所有点都被链接了,可以提前跳出循环。

Prim:

算法思想:贪心,从点出发。定义两个集合A储存所有的点,B储存选了的点。先随机确定一个起点放入集合A,每次从剩下的点中选择到A集合距离最小的点加入集合A。把所有点都放入A,将得到的距离求和。
复杂度:O(n^2) 适合稠密图

步骤:

  1. 以邻接矩阵储存图。
  2. 遍历,找出到集合A最小距离的点。并将这个点加入集合A,再更新图。

分割线

HDU-畅通工程AC代码:

Kruskal

#include 
using namespace std;
const int maxn=1E5+5;
const int INF=1<<30;
int n,m,cnt;
int pre[maxn];
struct node
{
    int from,to,val;
}qwe[maxn];
bool cmp (node a,node b)
{
    return a.val<b.val;
}
int unionsearch (int root)
{
    int son,tmp;
    son=root;
    while (root!=pre[root])
        root=pre[root];
    while (son!=root)
    {
        tmp=pre[son];
        pre[son]=root;
        son=tmp;
    }
    return root;
}
void join (int x,int y)
{
    if (unionsearch(x)!=unionsearch(y))
        pre[unionsearch(x)]=unionsearch(y);
    return;
}
void reset()
{
    for (int i=1;i<=n;i++)
        pre[i]=i;
}
int kruskal ()
{
    int mst=0;
    int x,y;
    int num=0;
    sort(qwe+1,qwe+1+m,cmp);
    reset();
    for (int i=1;i<=m;i++)
    {
        x=qwe[i].from;
        y=qwe[i].to;
        if (unionsearch(x)!=unionsearch(y))
        {
            mst+=qwe[i].val;
            num++;
            join(x,y);
            printf ("%d %d %d\n",qwe[i].from,qwe[i].to,qwe[i].val);
            if (num>=n-1)
                break;
        }
    }
    if (num<n-1)
        return -1;
    else
        return mst;
}
int main ()
{
    int a,b,c;
    while (~scanf ("%d",&m))
    {
        if (m==0)
            break;
        scanf ("%d",&n);
        for (int i=1;i<=m;i++)
        {
            scanf ("%d%d%d",&a,&b,&c);
            qwe[i].from=a;
            qwe[i].to=b;
            qwe[i].val=c;
        }
        if (kruskal()==-1)
            printf ("?\n");
        else
            printf ("%d\n",kruskal());
    }
    return 0;
}

prim

#include 
using namespace std;
const int maxn=1E3+5;
const int INF=1<<30;
int n,m,cnt;
int mp[maxn][maxn];
int mst[maxn];
int lowcost[maxn];
int prim(int start)
{
    int tmp,min_num,ans=0;
    mst[1]=1;
    for (int i=1;i<=n;i++)
    {
        lowcost[i]=mp[start][i];
        mst[i]=start;
    }
    mst[start]=-1;
    for (int i=1;i<n;i++)
    {
        min_num=INF;
        tmp=-1;
        for (int j=1;j<=n;j++)
        {
            if (mst[j]!=-1&&lowcost[j]<min_num)
            {
                tmp=j;
                min_num=lowcost[j];
            }
        }
        if (tmp==-1)
            return -1;
        else
        {
            mst[tmp]=-1;
            ans+=lowcost[tmp];
            for (int j=1;j<=n;j++)
            {
                if (mst[j]!=-1&&lowcost[j]>mp[tmp][j]) //如果在集合A中还有点的距离可以通过tmp这个点更新
                {
                    lowcost[j]=mp[tmp][j];//那么就更新
                    mst[j]=tmp;//并且到tmp这个点才是最小值
                }
            }
        }
    }
    return ans;
}
void reset()
{
    for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++)
        {
            if (i!=j)
                mp[i][j]=INF;
            else
                mp[i][j]=0;
        }
}
int main ()
{
    int a,b,c;
    while (~scanf ("%d",&m))
    {
        if (m==0)
            break;
        scanf ("%d",&n);
        reset();
        for (int i=1;i<=m;i++)
        {
            scanf ("%d%d%d",&a,&b,&c);
            mp[a][b]=min(mp[a][b],c);
            mp[b][a]=min(mp[b][a],c);
        }
        if (prim(1)==-1)
            printf ("?\n");
        else
            printf ("%d\n",prim(1));
    }
    return 0;
}

你可能感兴趣的:(求最小生成树两种方法总结(kruskal与prim))