prim算法基础详解(无向赋权图的最小生成树MST)

来源:互联网 发布:查看本机的mac地址 编辑:程序博客网 时间:2024/05/21 17:14

带权图分为有向和无向,无向图的最短路径又叫做最小生成树,有prime算法和kruskal算法

生成树的概念:联通图G的一个子图如果是一棵包含G的所有顶点的树,则该子图称为G的生成树 生成树是联通图的极小连通子图。

所谓极小是指:若在树中任意增加一条边,则将出现一个回路;若去掉一条边,将会使之变成非连通图。生成树各边的权值总和称为生成树的权。

权最小的生成树称为最小生成树,常用的算法有prime算法和kruskal算法。这里仅介绍prime算法..

算法描述如下:

1.将所有的结点分成两个集合。一部分是在最小生成树中的点(记为集合A),另一部分是不在最小生成树中的点(即待加入的点,记为集合B)。

2.开始的时候取任意一个点v作为顶点加入最小生成树中(A),然后遍历集合B,选取一个点vj使得(v,vj)的权值最小,然后将vj加入A集合,B集合中删去vj(删去操作可以用一个intree[i]数组来进行操作)

3.重复2步骤直到所有点遍历完毕。

注意:这里说的重复步骤2是指每次找的与顶点v的边权值最小的结点!

结合例子来看一下。(如下图)




选择1为初始结点,intree[1]标记为1.

遍历剩下的6个结点,找到(1,vj)中weight最小的那一个,显然是3,让3加入集合A,即intree[3]标记为1 



然后注意此时集合A中有了两个元素(结点1和结点3)开始的时候我认为是从3开始遍历找3与其他点的最小权值,但是再次注意我们每次只找与顶点(这里就1结点)边权值最小的点。

3结点入队后,1就可以与5结点间接相连了,所以每次在A中加入一个结点,都要更新顶结点与其他结点的关联与否以及权值。

这一点非常重要是算法的重要步骤。

正如此时(1,5) = 8  (1,4) = 5 (1,2) = 6 ,显然下一次应该加入A集合的是点4,所以:



接下来就是:












附上代码及测试样例。

[cpp] view plain copy
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #define maxn 10  
  4. #define INF  100000  
  5. int node;  
  6. int G[maxn + 1][maxn + 1];/*构建的图,必须是一个无向连通赋权图,G[i][j]存取权值*/   
  7. int intree[maxn];/*结点i在最小生成树中*/   
  8. int minweight[maxn];/*到i点的最小权重*/   
  9. int sumweight;  
  10.   
  11. void initialize()  
  12. {  
  13.     memset(intree,0,sizeof(intree));  
  14.     memset(minweight,0,sizeof(minweight));  
  15.     for(int i = 0 ; i <= maxn ; i++)  
  16.         for(int j = 0 ; j <= maxn ; j++)  
  17.             G[i][j] = INF;  
  18.     sumweight = 0;  
  19. }  
  20. void prime(int n)  
  21. {  
  22.     int node,Minweight;  
  23.       
  24.     for(int i = 1 ; i <= n ; i++)  
  25.         minweight[i] = G[1][i];  
  26.     /*先把1结点放进最小生成树,那么要知道1到其余结点的最小权重*/  
  27.     intree[1] = 1;/*1点进入集合A,最小生成树集*/   
  28.     for(int i = 2 ; i <= n ; i++)  
  29.     /*一共n个点,已经把1加入最小生成树,那么还剩下n-1个点*/   
  30.     {  
  31.         Minweight = INF;  
  32.         for(int j = 1 ; j <= n ; j++)  
  33.         {  
  34.             if(minweight[j] < Minweight && !intree[j])  
  35.             {  
  36.                 Minweight = minweight[j];  
  37.                 node = j;  
  38.             }  
  39.         }  
  40.         printf("选择的结点标号为:%d\n",node);   
  41.         intree[node] = 1;  
  42.         sumweight += Minweight;  
  43.         /*每次在A中加入新的点,更新顶结点到各节点的权重*/   
  44.         for(int j = 1 ; j <= n ; j++)  
  45.             if(!intree[j] && minweight[j] > G[node][j])  
  46.             /*更新B集合*/   
  47.                 minweight[j] = G[node][j];   
  48.     }  
  49. }  
  50. int main()  
  51. {  
  52.     int Node,Edge;  
  53.     int vertex1,vertex2,weight;  
  54.       
  55.     initialize();  
  56.     scanf("%d%d",&Node,&Edge);  
  57.     for(int i = 1 ; i <= Edge ; i++)  
  58.     {  
  59.         scanf("%d%d%d",&vertex1,&vertex2,&weight);  
  60.         G[vertex1][vertex2] = weight;  
  61.         G[vertex2][vertex1] = weight;  
  62.     }  
  63.     prime(Node);  
  64.     printf("%d\n",sumweight);  
  65.     return 0;  
  66. }  
  67. /* 
  68. 7 9 
  69. 1 2 6 
  70. 1 3 2 
  71. 1 4 5 
  72. 2 5 2 
  73. 2 7 4 
  74. 3 5 8 
  75. 4 6 7 
  76. 4 7 5 
  77. 5 6 10 
  78. 答案是25  
  79. */  


图例描述

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

c代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include<stdio.h>
#include<stdlib.h>
#definemax1000000000;
inta[1001][1001],d[1001],p[1001];
intmain(){
inti,j,k,m,n,min,ans,t;
intx,y,z;
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++){
scanf("%d%d%d",&x,&y,&z);
a[x][y]=z;
a[y][x]=z;
}
for(i=1;i<=n;i++)
d[i]=1000000000;
d[1]=0;
for(i=2;i<=n;i++){
min=max;
for(j=1;j<=n;j++)
if(!p[j]&&min>d[j])
min=d[j];
t=j;
}
p[t]=j;
for(j=1;j<=n;j++)
if(a[t][j]=0&&d[j]>a[t][j]){
d[j]=a[t][j];
ans+=min;
}
printf("%d",ans);
return0;
}



原创粉丝点击