定义1 对于无向图G和一棵树T来说,如果T是G的子图,则称T为G的树,如果T是G的生成子图,则称T是G的生成树。
定义2 对于一个边上具有权值的图来说,其边权值和最小的生成树称做图G的最小生成树。
定理1 对于一个图G,如果图中的边权值都不相同,则图的最小生成树唯一。
最小生成树
求无向图的最小生成树主要有Prim算法和Kruskal算法。
1.Prim算法
(1)基本算法
将图G中的所有点V分成两个顶点集合Va和Vb。在计算过程中Va中的点为已经选好连接入生成树的点,否则属于Vb。最开始的时候Va包含任意选取的图G中的一个点u,其余的点属于Vb,算法结束时所有与u连通的点属于Va,其余的点仍留在Vb中。如果算法结束时Vb不为空,说明图G的生成树不存在,只存在生成森林。
代码如下:
1 //直接实现,邻接矩阵存储图 2 const int maxn=101; 3 void Prim(int n,int dist[maxn],int map[maxn][maxn],int pre[maxn]) 4 //n个点,dist[i]表示向外延伸的最短边长,map记录图信息,pre[]记录连接信息, 5 //dist之和最最小权值 6 { 7 int i,j,k; 8 int min; 9 bool p[maxn];//记录该点是否属于Va 10 for(i=2;i<=n;i++) 11 { 12 p[i]=false; 13 dist[i]=map[1][i]; 14 pre[i]=1; 15 } 16 dist[1]=0; 17 p[1]=true; 18 for(i=1;i<=n-1;i++)//循环n-1次,每次加入一个点 19 { 20 min=INT_MAX; 21 k=0; 22 for(j=1;j<=n;j++) 23 { 24 if(!p[j]&&dist[j]<min) 25 { 26 min=dist[j]; 27 k=j; 28 } 29 } 30 if(k==0)return;//如果没有点可以扩展,即图G不连通,返回 31 p[k]=true; 32 for(j=1;j<=n;j++) 33 { 34 if(!p[j]&&map[k][j]!=INT_MAX&&dist[j]>map[k][j]) 35 { 36 dist[j]=map[k][j]; 37 pre[j]=k; 38 } 39 } 40 } 41 } 42 //时间复杂度O(n^2); 43 44 //堆实现 45 /*使用堆来保存Vb中每一点到Va中所有点的最短边长并维护其最小值, 46 并在访问每条边的时候更新。先将所有的点插入堆,并将值赋为inf, 47 将根赋值为0,通过松弛技术进行更新。*/ 48 //堆 49 struct HeapElement 50 { 51 int key,value; 52 }; 53 struct MinHeap 54 { 55 HeapElement H[maxn]; 56 int size; 57 int position[maxn]; 58 void init(){H[size=0].value=-INF;} 59 void ins(int key,int value) 60 void decrease(int key,int value) 61 void delmin() 62 }H; 63 //边 64 struct edge 65 { 66 int to,w,next; 67 }edge[maxm]; 68 int N,M; 69 long long dist[maxn]; 70 int head[maxn]; 71 void Prim() 72 { 73 int i,j,k; 74 bool p[maxn]={0}; 75 H.init(true); 76 for(i=1;i<=N;i++) 77 { 78 H.ins(i,INF); 79 dist[i]=INF; 80 } 81 dist[1]=0; 82 H.decrease(1,0); 83 for(i=1;;) 84 { 85 p[i]=true; 86 H.delmin();//删除堆顶元素 87 for(k=head[i];k!=-1;k=edge[k].next) 88 { 89 if(!p[j]&&edge[k].w<dist[j=edge[k].to]) 90 { 91 dist[j]=edge[k].w; 92 H.decrease(j,dist[j]); 93 } 94 } 95 if(H.size)i=H.H[1].key; 96 //扩展Vb中的点,以便下次更新 97 else break; 98 } 99 } 100 101 /*用堆优化的Prim算法主要用于加速变较少的图的最小生成树的计算,特别是稀疏图。 102 总的时间复杂度是O((n+m)log n).当边较少时,这种算法相对直接实现的Prim算法来说 103 有很好的效果。*/