最小生成树两种算法比较与实现

Kruskal算法 :(并查集)
时间复杂度O(elog2e),适合简单图。
算法步骤:

  1.构造一个有n个顶点的无边子图;

  2.从原图选择边权最小的边加入该子图,直至子图成为一棵树;

  3.边能加入子图的条件是,边的两个端点u,v还未连通,Kruskal算法中运用并查集的查询来询问两个顶点是否连通;

  Kruskal算法的本质是,通过树的合并(不断加边,构成子树),来构建完整的生成树。

就是先将边的权按从小到大的顺序排起来,依次选出边,判断两点是否在一个集合中,如果在,看下一条边,如果不在,则将两点合并,这条边为最小生成树的一条边。

#include
#include
#include
#include
using namespace std;
const int MAXN = 505;
int a[MAXN][MAXN];
typedef struct graph{
   int u,v,cost;
   friend bool operator <(const graph &a,const graph &b)
   {
       return a.costint b[MAXN];
int c[MAXN];
int find2(int x)
{
    int i = x;
    while(x != b[x])
    {
        x = b[x];
    }
    int p;
    while(i != b[i])
    {
        p = b[i];
        b[i] = x;
        i = p;
    }
    return x;
}
void find1(int n,int m)
{
    int p = find2(n);
    int q = find2(m);
    if(p!=q)
    {
        b[q]=b[p];
    }
}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n,i,j;
        scanf("%d",&n);
        for(i = 1;i <= n;++i)
        {
            b[i] = i;
        }
        for(i=1;i<=n;++i)
        {
            for(j=1;j<=n;++j)
            {
                scanf("%d",&a[i][j]);
            }
        }
        int k=1;
        for(i = 1;i <= n;++i)
        {
            for(j = i + 1;j <= n;++j)
            {
                edge[k].u = i;
                edge[k].v = j;
                edge[k].cost = a[i][j];
                ++k;
            }
        }
        sort(edge+1,edge+k);
        /*for(i = 1;i < k;++i)
        {
            printf("%d %d %d\n",edge[i].u,edge[i].v,edge[i].cost);
        }*/
        int ptr = 0;
        for(i = 1;i < k;++i)
        {
            if(find2(edge[i].u)!=find2(edge[i].v))
            {
                find1(edge[i].u,edge[i].v);
                c[ptr++] = i;
            }
        }
        int mx = 0;
        for(i = 0;i < ptr;++i)
        {
            mx = (mx < edge[c[i]].cost)?edge[c[i]].cost:mx;
        }
        printf("%d\n",mx);
    }
    return 0;
}

Prim算法:(离散书上讲的方法)
时间是复杂度O(n2),适合稠密图。
Prim算法的基本步骤:

  1.初始化点集 V={x};

  2.找到边(u,v)满足:u∈点集V,v不∈点集V;

  3.选取2.中满足条件的边中最小的一条加入生成树,并把v加入点集V,重复执行,直至原图所有的点都加入点集V,就得到了一棵最小生成树。

Tips:关于如何快速找到可以添加到生成树的边:

                可以维护一个数组lowcost[i…j]记录点集V到各个顶点的最小边,即可快速找到边,且每当有新的点加入点集V时,该数组都要更新一次。
  #include
#include
#include
#include
using namespace std;
const int MAXN = 2005;
const int inf = 0x3f3f3f3f;
int mp[MAXN][MAXN];
int lowcost[MAXN];
bool vis[MAXN];
int Prime(int n)
{
    int i,j;
    for(i = 1;i <= n;++i)
    {
        lowcost[i] = mp[1][i];
    }
    vis[1] = true;
    lowcost[1] = 0;
    int ans = 0;
    for(i = 0;i < n - 1;++i)
    {
        int min = inf;
        int k = 0;
        for(j = 1;j <= n;++j)
        {
            if(!vis[j]&&min > lowcost[j])
            {
                min = lowcost[j];
                k = j;
            }
        }
        vis[k] = true;
        ans = (ans > min)?ans:min;
        for(j = 1;j <= n;++j)
        {
            if(!vis[j]&&lowcost[j] > mp[k][j])
                 lowcost[j] = mp[k][j];
        }
    }
    return ans;
}
int main()
{
    int n,m;
    while(~scanf("%d %d",&n,&m))
    {
        int i,j;
        memset(mp,inf,sizeof(mp));
        memset(vis,false,sizeof(vis));
        for(i = 0;i < m;++i)
        {
            int x,y,z;
            scanf("%d %d %d",&x,&y,&z);
            if(mp[x][y] > z)//防止出现重边现象
                mp[x][y] = z;
            if(mp[y][x] > z)
                mp[y][x] = z;
        }
        int ans = Prime(n);
        printf("%d\n",ans);
    }
    return 0;
}  

你可能感兴趣的:(ACM__图论及其应用)