图的算法专题——最小生成树

概要:

  1. Prim算法
  2. Kruskal算法

1、Prim算法

算法流程:

(1)对图G(V,E)设置集合S来存放已被并入的顶点,然后执行n次(2)(3)

(2)每次从未并入顶点集合中选择与集合S最近的一个顶点,访问u并将其加入集合S,同时把这条离集合S最近的边加入最小生成树中。

(3)优化松弛

Prim算法与Dijkstra算法使用的思想几乎完全相同,只有在数组d[]的含义上有区别。(关于Dijkstra算法具体可以看前面写的《图的算法专题——最短路径》)

Dijkstra中d[]含义为起点s到达顶点Vi的最短距离,而Prim算法d[]含义为顶点Vi与集合S的最短距离,两者区别仅在于最短距离是到 起点 还是 集合 。

邻接矩阵版:

 1 const int maxn=1000;
 2 const int INF=1000000000;
 3 int n,G[maxn][maxn];
 4 int d[maxm]; //顶点到集合S的距离
 5 bool vis[maxn]={false};
 6 
 7 int prim(){ //默认0号为初始点,函数返回最小生成树的边权之和
 8     fill(d,d+maxn,INF);
 9     d[0]=0;
10     int ans=0; //边权之和
11     for(int i=0;i<n;i++){
12         int u=-1,MIN=INF;
13         for(int j=0;j<n;j++){
14             if(vis[j]==false&&d[j]<MIN){
15                 u=j;
16                 MIN=d[j];
17             }
18         }
19         if(u==-1) return -1;
20         vis[u]=true;
21         ans+=d[u]; //将与集合S距离最小的边加入最小生成树
22         for(int v=0;v<n;v++){
23             if(vis[v]==false && G[u][v]!=INF && G[u][v]<d[v] ){  
24                 d[v]=G[u][v]; //以u为中介点可以使v离集合S更近(u已被并入集合)
25             }
26         }
27     }
28     return ans;
29 }

邻接表版:

 1 struct Node{
 2     int v,dis;  //v为目标顶点,dis为边权
 3 };
 4 vector<Node> Adj[maxn];
 5 int n,d[maxn];
 6 bool vis[maxn]={false};
 7 int prim(){
 8     fill(d,d+maxn,INF);
 9     d[0]=0;
10     int ans=0;
11     for(int i=0;i<n;i++){
12         int u=-1,MIN=INF;
13         for(int j=0;j<n;j++){
14             if(vis[j]==false && d[j]<MIN){
15                 u=j;
16                 MIN=d[j];
17             }
18         }
19         if(u==-1) return -1;
20         vis[u]=true;
21         ans+=d[u];
22         for(int j=0;j<Adj[u].size();j++){
23             int v=Adj[u][j].v;
24             if(vis[v]==false && Adj[u][j].dis< d[v]){
25                 d[v]=Adj[u][j].dis;
26             }
27         }
28     }
29     return ans;
30 }

2、Kruskal算法

算法流程:

(1)对所有边权从小到大排序。

(2)按边权从小到大测试所有边,如果当前测试边所连接的两个顶点不在同一个连通块中,则并入,否则舍弃。 (运用并查集)

(3)循环执行(2)直到最小生成树的边数等于总顶点数减1或是测试完所有边时结束。若结束时边数不为顶点数减1,则图不连通。

 1 const int MAXV=110;
 2 const int MAXE=10010;
 3 int father[MAXV];
 4 int findFather(int x){
 5     int a=x;
 6     while(x!=father[x]){
 7         x=father[x];
 8     }
 9     while(a!=father[a]){
10         int z=a;
11         a=father[a];
12         father[z]=x;
13     }
14     return x;
15 }
16 struct edge{
17     int u,v; //边的两个端点编号
18     int cost; //边权
19 }E[MAXE];
20 bool cmp(edge a,edge b){
21     return a.cost<b.cost;
22 }
23 int kruskal(int n,int m){  //n为顶点个数,m为图的边数
24     int ans=0,Num_Edge=0;
25     for(int i=1;i<=n;i++) father[i]=i; //假设顶点范围为1到n 
26     sort(E,E+m,cmp);
27     for(int i=0;i<m;i++){ //枚举边
28         int faU=findFather(E[i].u);
29         int faV=findFather(E[i].v);
30         if(faU!=faV){
31             father[faU]=faV;
32             ans+=E[i].cost;
33             Num_Edge++;
34             if(Num_Edge==n-1) break;
35         }
36     }
37     if(Num_Edge != n-1) return -1;
38     else return ans;
39 }

猜你喜欢

转载自www.cnblogs.com/Mered1th/p/10422955.html
今日推荐