最小生成树--Prim算法和Kruskal算法

来源:互联网 发布:tensorflow spark 编辑:程序博客网 时间:2024/06/10 00:04

概述

Prim算法和Kruskal算法,是用来求加权连通图的最小生成树的算法。

意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点,且其所有边的权值之和亦为最小。

—————————————————————————————————————————————————————————————————————————————

Prim算法

基本思想

从单一顶点开始,普里姆算法按照以下步骤逐步扩大树中所含顶点的数目,直到遍及连通图的所有顶点。

  1. 输入:一个加权连通图,其中顶点集合为V,边集合为E;

  2. 初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {};

  3. 重复下列操作,直到Vnew = V:

    1. 在集合E中选取权值最小的边(u, v),其中u为集合Vnew中的元素,而v则是V中没有加入Vnew的顶点(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);

    2. 将v加入集合Vnew中,将(u, v)加入集合Enew中;

  4. 输出:使用集合Vnew和Enew来描述所得到的最小生成树。


示例:

 说明不可选可选已选Prim Algorithm 0.svg此为原始的加权连通图。每条边一侧的数字代表其权值。---Prim Algorithm 1.svg顶点D被任意选为起始点。顶点ABEF通过单条边与D相连。A是距离D最近的顶点,因此将A及对应边AD以高亮表示。C, GA, B, E, FDPrim Algorithm 2.svg下一个顶点为距离DA最近的顶点。BD为9,距A为7,E为15,F为6。因此,FDA最近,因此将顶点F与相应边DF以高亮表示。C, GB, E, FA, DPrim Algorithm 3.svg算法继续重复上面的步骤。距离A为7的顶点B被高亮表示。CB, E, GA, D, FPrim Algorithm 4.svg在当前情况下,可以在CEG间进行选择。CB为8,EB为7,GF为11。E最近,因此将顶点E与相应边BE高亮表示。无C, E, GA, D, F, BPrim Algorithm 5.svg这里,可供选择的顶点只有CGCE为5,GE为9,故选取C,并与边EC一同高亮表示。无C, GA, D, F, B, EPrim Algorithm 6.svg顶点G是唯一剩下的顶点,它距F为11,距E为9,E最近,故高亮表示G及相应边EG。无GA, D, F, B, E, CPrim Algorithm 7.svg现在,所有顶点均已被选取,图中绿色部分即为连通图的最小生成树。在此例中,最小生成树的权值之和为39。无无A, D, F, B, E, C, G

算法实现:

#include<iostream>using namespace std;const int INF = 999999;const int MAXV = 10000;int vnum, edgenum, start;// 邻接矩阵 int edge[MAXV][MAXV];// 记录Vnew中每个点到V中邻接点的最短边int lowcost[MAXV];// 标记某点是否加入Vnewint addVnew[MAXV];// 记录V中与Vnew最邻近的点int adj[MAXV];void prim(int start){// 最小生成树的权值 int sumweight = 0;int i, j;int k = 0;for(i=1;i<=vnum;++i){lowcost[i] = INF;adj[i] = start;}// 顶点从1开始 for(i=1;i<=vnum;++i){// 将所有点至于Vnew之外,V之内// 这里只要addVnew对应的值为-1,就表示在Vnew之外lowcost[i] = edge[start][i];addVnew[i] = -1;}// 将起始点start加入VnewaddVnew[start] = 0;adj[start] = start;for(i=1;i<=vnum;++i){// 由于从start开始的,因此不需要再对第start个顶点进行处理if(start==i)continue;int min = INF;int v = -1;for(j=1;j<=vnum;++j){// 在Vnew之外寻找最短路径if(addVnew[j]==-1 && lowcost[j]<min){min = lowcost[j];v = j;}}if(v!=-1){// 输出新加入的边 cout<<adj[v]<<" "<<v<<" "<<lowcost[v]<<endl;// 将v加Vnew中addVnew[v] = 0;// 计算路径长度之和sumweight += lowcost[v];for(j=1;j<=vnum;++j){if(addVnew[j]==-1 && edge[v][j]<lowcost[j]){// 此时v点加入Vnew 需要更新lowcostlowcost[j] = edge[v][j];adj[j] = v;}}}}// 输出最小生成树的权值 cout<<sumweight<<endl;}int main(){while(cin>>vnum>>edgenum>>start){for(int i=1;i<=vnum;++i){for(int j=1;j<=vnum;++j){if(i==j)edge[i][j] = 0;elseedge[i][j] = INF;}}int p, q, val;for(int i=1;i<=edgenum;++i){cin>>p>>q>>val;edge[p][q] = val;edge[q][p] = val;}prim(start);}return 0;}

测试案例:

输入

7 11 41 2 71 4 52 3 82 4 92 5 73 5 54 5 154 6 65 6 85 7 96 7 11

输出

4 1 54 6 61 2 72 5 75 3 55 7 939



————————————————————————————————————————————————————————————————————————————

Kruskal算法

基本思想:

按照权值从小到大选择n-1条边,且保证这n-1条边不形成回路。

首先构造一个只含有n个顶点的森林,然后依照权值从小到大从联通网中选择边加入到深林中,并使森林不产生回路,直至森林变成一棵树为止。


示例:



要点:

1)对图的所有边按照权值从小到大进行排序

解决方法:采用排序算法进行排序

2)将边添加到最小生成树中时,判断是否形成了回路

解决方法:记录顶点在最小生成树中的终点,然后每次需要将一条边添加到最小生成树时,判断该边的两个顶点的终点是否重合,重合的话会构成回路。


伪代码:

Kruskal(){    对边的权值从小到大进行排序;    初始化每个顶点的终点为它自己;    对于所有的边(u,v):        找到顶点u的终点x;        找到顶点v的终点y;        如果(x!=y):            将边(u,v)加入到最小生成树中;            修改顶点u的终点;}

算法实现:

#include<iostream>#include<algorithm>using namespace std;// 最大边的数量 const int MAXE = 20000;// 最大顶点的数量const int MAXV = 20000; // 边的结构体 struct edge{int u, v, cost;};// 数组,用来保存所有的边 edge road[MAXE];// 数组,用来保存对应顶点的终点 int endpoint[MAXV];// sort需要的比较函数 bool cmp(const edge &e1, const edge &e2){      return e1.cost<e2.cost;  }// find函数返回顶点x的终点 int find(int x){      if (endpoint[x] == x) return x;      else return endpoint[x] = find(endpoint[x]);  }  // unite函数的结果是将顶点x和顶点y加入到最小生成树中 void unite(int x, int y){      x = find(x);      y = find(y);      // 终点一样则不发生变化     if (x == y) return;      // 否则,修改x的终点为y     else endpoint[x] = y;  }  // init函数的结果是将所有顶点的终点初始化为自己 void init(int vexnum){for(int i=0;i<vexnum;++i)endpoint[i] = i;}int KRUSKAL(int vexnum, int edgenum){// ans用于记录最小生成树的总权值 int ans = 0;// 初始化每个顶点的终点为它自己init(vexnum);//  对边的权值从小到大进行排序sort(road, road+edgenum, cmp);for(int i=0;i<edgenum;i++){          if(find(road[i].u)!=find(road[i].v)){          // 将边(u,v)加入到最小生成树中            // 修改顶点u的终点;            unite(road[i].u, road[i].v);              ans += road[i].cost;          }      }return ans;}int main(){int vexnum, edgenum;cin>>vexnum>>edgenum;for(int i=0;i<edgenum;++i){cin>>road[i].u>>road[i].v>>road[i].cost;}cout<<KRUSKAL(vexnum, edgenum)<<endl;return 0;}

测试案例:

输入

7 110 1 70 3 51 2 81 3 91 4 72 4 53 4 153 5 64 5 84 6 95 6 11

输出

39

参考资料:

维基百科、如果天空不死--博客园、华山大师兄--博客园

0 0
原创粉丝点击