最小生成树-prim算法

Posted taming

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最小生成树-prim算法相关的知识,希望对你有一定的参考价值。

prim算法和kruskal算法一样,都是应用贪心策略,prim算法是以点为对象,最小生成树会连通N个节点,每次把离不完整的最小生成树距离最近的一个节点连通到最小生成树直到连通全部节点。

最小生成树的出发点可以是任意一个节点(一般选第一个节点),然后找离最小生成树最近的节点,只是如果是暴力枚举的话时间复杂度会有O(n^3),使用记忆化的技巧,定义一个记录每个节点到最小生成树距离的数组lowdis,初始情况为每个节点离起点的直接路径长度(没有边直接连通就初始化为无限大)。然后每次加入一个点检查遍造成的影响,比如新加入一个节点p,可能且仅可能出现lowdis[i]>dis[i][p]会对lowdis数组数据的正确性造成影响。根据这个检查一遍lowdis数组就能保证lowdis严格代表每个未加入的节点到最小生成树的距离。

画个图:

技术分享图片

假设从1节点开始,当前被生成树上只有1节点,剩下未被连通的节点 又和已经被生成树连通的节点也就是1节点直接相连的节点有2,3,4节点,距离最短的是4节点。把4节点连接生。

现在已经被连通的节点是1节点和4节点,和1节点或者4节点直接连通的节点有2,3,5,6节点,其中距离最短的节点是节点2,连通节点2。

同理,依次连通7节点,3节点,5节点,6节点,结束。

方法就是这样。

算法实现:

用邻接矩阵dis存储整张图,dis[i][j]代表 i 到 j 节点的距离。

假设lowdis[i]存放的是为被连通的节点 i 到已经连通节点的最短距离,则通过扫描一遍lowdis数组就能在O(n)的时间内找到距离最短的节点 minn 。

初始情况下也就是只连通一个节点的情况下  lowdis[i]=dis[1][i](dis存放的是两点间的距离,如果 i,j 不直接连通就把dis[i][j]初始化为无限大)。

现在假设又在原来的基础上连通了节点k,则lowdis[i]=min(dis[k][i],lowdis[i])。

当一个新的节点加入后,唯一会造成的影响就是节点 i 到节点 k 的距离更短,所以只需要稍微更新一下lowdis数组就能维持lowdis记录最短距离的正确性不变。

需要注意的是循环终止条件:当全部节点被连通

无解的情况:剩余的没有被连通的节点到已经连通节点的距离全为无限大,也就是图不连通,无解。

完整示例(hdu-1863):

 

[cpp] view plain copy
 
  1. #include<iostream>  
  2. #include<cstring>  
  3. using namespace std;  
  4.   
  5. #define MaxN 100  
  6. #define INF 100000  
  7.   
  8. int dis[MaxN][MaxN];  
  9. int lowdis[MaxN];  
  10. int N;  
  11.   
  12. void init(){  
  13.    for(int i=0;i<MaxN;i++){  
  14.       for(int j=0;j<MaxN;j++){  
  15.          dis[i][j]=INF;  
  16.       }  
  17.    }  
  18. }  
  19.   
  20. int prim(){  
  21.    int ans=0;  //记录题目所求的最小生成树边的权值的和  
  22.    bool flag[MaxN]={0}; //标记每个节点是否被连通  
  23.    for(int i=2;i<=N;i++){  //初始化lowdis数组  
  24.       lowdis[i]=dis[1][i];  
  25.    }  
  26.    int mindis,minn;  
  27.    for(int i=1;i<N;i++){  //每次找出一个节点  当找出N个节点终止循环  
  28.       mindis=INF;  
  29.       for(int j=2;j<=N;j++){  
  30.          if(lowdis[j]<mindis&&flag[j]==false){  
  31.             mindis=lowdis[j];  
  32.             minn=j;  //记录最近节点的信息  
  33.          }  
  34.       }  
  35.       if(mindis<INF){  
  36.          ans+=mindis;  
  37.          flag[minn]=true;  
  38.          for(int j=2;j<=N;j++){  //成功连通一个节点后更新lowdis数组  
  39.             if(dis[minn][j]<lowdis[j]){  
  40.                lowdis[j]=dis[minn][j];  
  41.             }  
  42.          }  
  43.       }  
  44.       else{  //图不连通的情况  
  45.          return -1;  
  46.       }  
  47.    }  
  48.    return ans;  
  49. }  
  50.   
  51. int main(){  
  52.    int EdgeNum,a,b,c;  
  53.    while(cin>>EdgeNum>>N&&EdgeNum){  
  54.       init();  
  55.       for(int i=0;i<EdgeNum;i++){  
  56.          cin>>a>>b>>c;  
  57.          dis[a][b]=dis[b][a]=c;  
  58.       }  
  59.       int ans=prim();  
  60.       if(ans==-1){  
  61.          cout<<‘?‘<<endl;  
  62.       }  
  63.       else{  
  64.          cout<<ans<<endl;  
  65.       }  
  66.    }  
  67. }  

以上是关于最小生成树-prim算法的主要内容,如果未能解决你的问题,请参考以下文章

prim和kruscal算法得到的最小生成树是不是一样

图的最小生成树算法(Prim和Kruskal)

最小生成树prim()算法;

最小生成树-prim算法

图的最小生成树算法?

急!数据结构最小生成树prim算法C语言实现