Minimum Spanning Tree
生成树
如果连通图G的一个子图是一棵包含G的所有顶点的树,则该子图称为G的生成树(Spanning Tree)。
图的生成树不惟一。
最小生成树
生成树T各边的权值总和称为该树的权;权最小的生成树称为G的最小生成树(Minimum SpannirngTree)。最小生成树可简记为MST。
MST性质:
假设N=(V,{E})是一个连通网,U是顶点集V的一个非空子集。若(u,v)是一条具有最小权值(代价)的边,其中u∈U,v ∈V-U,则最小生成树中必包含边(u,v)。
证明(反证法):(u,v)是一条具有最小权值(代价)的边,如果最小生成树不包含它,势必要包含其他边,以使U和V-U两个集合连通。无论是包含其他边中的哪一条,其权值都会比(u,v)更大,导致总的权值之和也更大。
算法:
算法一:
Prim算法(普里姆算法)
基本思想:取图中任意一个顶点 v 作为生成树的根,之后往生成树上添加新的顶点 w。在添加的顶点 w 和已经在生成树上的顶点v 之间必定存在一条边,并且该边的权值在所有连通顶点 v 和 w 之间的边中取值最小。之后继续往生成树上添加顶点,直至生成树上含有 n-1 个顶点为止。
适用于稠密图。
算法二:
Kruskal算法(克鲁斯卡尔算法)
基本思想:考虑问题的出发点: 为使生成树上边的权值之和达到最小,则应使生成树中每一条边的权值尽可能地小。具体做法: 先构造一个只含 n 个顶点的子图 SG,然后从权值最小的边开始,若它的添加不使SG 中产生回路,则在 SG 上加上这条边,如此重复,直至加上 n-1 条边为止。
适用于稀疏图。
以下是代码:
1 void Prim(int n, int **weight) 2 { 3 int lowest_weight[n+1]; 4 int closest_vertex[n+1]; 5 bool has_been_added[n+1]; 6 has_been_added[1] = true; 7 for (int i = 2; i <= n; i++) // 初始化 8 { 9 lowest_weight[i] = weight[1][i]; 10 closest_vertex[i] = 1; 11 has_been_added[i] = false; 12 } 13 for (int i = 1; i < n; i++) 14 { 15 int lowest_weight_of_all_edges = inf; // 把当前最小权定义为无限大 16 int vertex_will_be_added = 1; 17 for (int j = 2; j <= n; j++) // 找出本次添加的点 18 { 19 if ((!has_been_added[j])&&(lowest_weight[j]<lowest_weight_of_all_edges)) 20 { 21 lowest_weight_of_all_edges = lowest_weight[j]; 22 vertex_will_be_added = j; 23 } 24 } 25 has_been_added[j] = true; 26 cout<<"this time we add : "<<vertex_will_be_added<<" by "<<closest_vertex[vertex_will_be_added]<<endl; 27 for (int j = 2; j <=n ; j++) // 更新最小权 28 { 29 if ((!has_been_added[j])&&(weight[vertex_will_be_added][j]<lowest_weight[j])) 30 { 31 lowest_weight[j] = weight[vertex_will_be_added][j]; 32 closest_vertex[j] = vertex_will_be_added; 33 } 34 } 35 } 36 }
kruskal代码待更新。