Prim和Kruskal算法

Prim和Kruskal算法

普里姆(Prim)算法——基本思想:

设G=(V, E)是具有n个顶点的连通网,
T=(U, TE)是G的最小生成树,
T的初始状态为U={u0}(u0∈V),TE={ },
重复执行下述操作:
在所有u∈U,v∈V-U的边中找一条代价最小的边(u, v)并入集合TE,同时v并入U,直至U=V。

Prim算法——伪代码:
  1. 初始化两个辅助数组lowcost(=arc[0][i])和adjvex(=0)(0是始点);
  2. 输出顶点u0,将顶点u0加入集合U中;
  3. 重复执行下列操作n-1次
    3.1 在lowcost中选取最短边(lowcost[k]),取对应的顶点序号k;
    3.2 输出顶点k和对应的权值;
    3.3 将顶点k加入集合U中(lowcost[k]=0);
    3.4 调整数组lowcost和adjvex;
Void prime(MGraph G){
    for(int i=1;i<G.vertexNu;i++){
        lowcost[i]=G.arc[0][i];  adjvex[i]=0;
    }
    lowcost[0]=0;
    for(i=1;i<G.vertexNum;i+++){
        k=MinEdge(lowcost,G.vertexNum)
        cout<<K<<adjvex[k]<<lowcost[k];
        lowcost[k]=0;
               for(j=1;j<G.vertexNum;j++)
          if((G.arc[k][j]<lowcost[j]){
              lowcost[j]=G.arc[k][j];
              arcvex[j]=k;
           }
    }
}

克鲁斯卡尔(Kruskal)算法 ——基本思想:

1.设无向连通网为G=(V, E),令G的最小生成树为T=(U, TE),其初态为U=V,TE={ },
2.然后,按照边的权值由小到大的顺序,考察G的边集E中的各条边。
(1)若被考察的边的两个顶点属于T的两个不同的连通分量,则将此边作为最小生成树的边加入到T中,同时把两个连通分量连接为一个连通分量;
(2)若被考察边的两个顶点属于同一个连通分量,则舍去此边,以免造成回路,
3.如此下去,当T中的连通分量个数为1时,此连通分量便为G的一棵最小生成树。

克鲁斯卡尔(Kruskal)算法 ——伪代码:
  1. 初始化:U=V; TE={ };
  2. 循环直到T中的连通分量个数为1
    2.1 在E中寻找最短边(u,v);
    2.2 如果顶点u、v位于T的两个不同连通分量,则
    2.2.1 将边(u,v)并入TE;
    2.2.2 将这两个连通分量合为一个;
    2.3 在E中标记边(u,v),使得(u,v)不参加后续最短边的选取;

关键问题:
1.图的存储结构
采用边集数组存储图。
2.如何判断一条边所依附的两个顶点在同一个连通分两中
定义Parent[i]数组,辅助完成连通分量的处理。数组分量的值表示顶点i的双亲节点(初值为-1;)
当一条边(u,v)的两个顶点的根结不同时,这两个结点属于不同的连通分量(利用parent 数组查找一棵树的根节点。当一个结点n的parent==-1,树的根节点即为n)
3. 如何将一条边所依附的两个顶点合并到同一个连通分量中
要进行联通分量的合并 ,其中一个顶点所在的树的根节点为vex1,另一个顶点所在的树的根节点为vex2,则:parent[vex2]=vex1;

  for (k=0;k<arcNum;k++)	{
         begin=edge[k].from;	end=edge[k].to;	
         int m,n;
        m=Find(parent,begin);	n=Find(parent,end);
        if(m!=n)	{
            cout<<begin<<","<<end<<","<<edge[k].weight<<endl;
            parent[n]=m;	count++;
            if(count==vertexNum-1)	break;
       }
   }
   return 0;
}
int Find(int *parent, int node)
{
	int f;
	f=node;
	while(parent[f]>-1)
		f=parent[f];
	return f;
}

Prim 完整代码:

#include<iostream>
#define INF 10000
using namespace std;
constint N = 6;
bool visit[N];
intdist[N] = { 0, };
intgraph[N][N] = { {INF,7,4,INF,INF,INF},  //INF代表两点之间不可达
                    {7,INF,6,2,INF,4},
                    {4,6,INF,INF,9,8},
                    {INF,2,INF,INF,INF,7},
                    {INF,INF,9,INF,INF,1},
                    {INF,4,8,7,1,INF}
                  };
intprim(intcur)
{
    intindex = cur;
    intsum = 0;
    inti = 0;
    intj = 0;
    cout << index << " ";
    memset(visit,false, sizeof(visit));
    visit[cur] = true;
    for(i = 0; i < N; i++)
        dist[i] = graph[cur][i];//初始化,每个与a邻接的点的距离存入dist
    for(i = 1; i < N; i++)
    {
        intminor = INF;
        for(j = 0; j < N; j++)
        {
            if(!visit[j] && dist[j] < minor)          //找到未访问的点中,距离当前最小生成树距离最小的点
            {
                minor = dist[j];
                index = j;
            }
        }
        visit[index] = true;
        cout << index << " ";
        sum += minor;
        for(j = 0; j < N; j++)
        {
            if(!visit[j] && dist[j]>graph[index][j])      //执行更新,如果点距离当前点的距离更近,就更新dist
            {
                dist[j] = graph[index][j];
            }
        }
    }
    cout << endl;
    returnsum;               //返回最小生成树的总路径值
}
intmain()
{
    cout << prim(0) << endl;//从顶点a开始
    return0;
}

Kruskal完整代码:

#include<iostream>
#define N 7
using namespace std;
typedef struct _node{
    intval;
    intstart;
    intend;
}Node;
Node V[N];
intcmp(constvoid *a, constvoid *b)
{
    return(*(Node *)a).val - (*(Node*)b).val;
}
intedge[N][3] = {  { 0,1,3},
                    {0,4,1}, 
                    {1,2,5}, 
                    {1,4,4},
                    {2,3,2}, 
                    {2,4,6}, 
                    {3,4,7}
                    };
 
intfather[N] = { 0, };
intcap[N] = {0,};
 
voidmake_set()              //初始化集合,让所有的点都各成一个集合,每个集合都只包含自己
{
    for(inti = 0; i < N; i++)
    {
        father[i] = i;
        cap[i] = 1;
    }
}
 
intfind_set(intx)              //判断一个点属于哪个集合,点如果都有着共同的祖先结点,就可以说他们属于一个集合
{
    if(x != father[x])
     {                             
        father[x] = find_set(father[x]);
    }    
    returnfather[x];
}                                 
 
voidUnion(intx, inty)         //将x,y合并到同一个集合
{
    x = find_set(x);
    y = find_set(y);
    if(x == y)
        return;
    if(cap[x] < cap[y])
        father[x] = find_set(y);
    else
    {
        if(cap[x] == cap[y])
            cap[x]++;
        father[y] = find_set(x);
    }
}
 
intKruskal(intn)
{
    intsum = 0;
    make_set();
    for(inti = 0; i < N; i++)//将边的顺序按从小到大取出来
    {
        if(find_set(V[i].start) != find_set(V[i].end))     //如果改变的两个顶点还不在一个集合中,就并到一个集合里,生成树的长度加上这条边的长度
        {
            Union(V[i].start, V[i].end);  //合并两个顶点到一个集合
            sum += V[i].val;
        }
    }
    returnsum;
}
intmain()
{
    for(inti = 0; i < N; i++)   //初始化边的数据,在实际应用中可根据具体情况转换并且读取数据,这边只是测试用例
    {
        V[i].start = edge[i][0];
        V[i].end = edge[i][1];
        V[i].val = edge[i][2];
    }
    qsort(V, N, sizeof(V[0]), cmp);
    cout << Kruskal(0)<<endl;   

加油,向未来!!!

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

莫余

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值