【算法复习】图的最小生成树(Prim&Kruskal)
所谓生成树就是
如果连通图G的一个子图是一棵包含G的所有顶点的树,则该子图称为G的生成树。
生成树是连通图的包含图中的所有顶点的极小连通子图。
(图的生成树不惟一。从不同的顶点出发进行遍历,可以得到不同的生成树)
而权值最小的树就是最小生成树。
关于生成树最经典的应用模型就是沟通零散点最小造价的问题,
比如网络G表示n各城市之间的通信线路网线路(其中顶点表示城市,边表示两个城市之间的通信线路,边上的权值表示线路的长度或造价)
通过求该网络的最小生成树达到求解通信线路或总代价最小的最佳方案
求图的最小生成树主要有两种经典算法:
1.普里姆算法时间复杂度为O(n2).适合于求边稠密的最小生成树。
2.克鲁斯卡尔算法时间复杂度为O(eloge)(e为网中边数),适合于求稀疏的网的最小生成树。
普里姆算法
通过邻接矩阵图表示的简易实现中,找到所有最小权边共需O(V2)的运行时间。使用简单的二叉堆与邻接表来表示的话,普里姆算法的运行时间则可缩减为O(E log V),其中E为连通图的边数,V为顶点数。如果使用较为复杂的斐波那契堆,则可将运行时间进一步缩短为O(E + V log V),这在连通图足够密集时(当E满足Ω(V log V)条件时),可较显著地提高运行速度。
算法思想:取图中任意一个顶点V作为生成树的根,之后若要往生成树上添加顶点W,则在顶点V和W之间必定存在一条边。并且该边的权值在所有连通顶点V和W之间的边中取值最小。
以下代码采用的数据存储方式为邻接矩阵
typedef struct{ int begin; int end; int weight;}Edge; //定义边集结构void sort_edges(Edge& edge,Edge& edge2) //交换大小,为后面排序所用{ Edge edge_temp; edge_temp.begin = edge.begin; edge_temp.end = edge.end; edge_temp.weight = edge.weight; edge.begin = edge2.begin; edge.end = edge2.end; edge.weight = edge2.weight; edge2.begin = edge_temp.begin; edge2.end = edge_temp.end; edge2.weight = edge_temp.weight;}void turn_to_edges(MGraph &G,Edge* edges) //讲邻接矩阵转换成边集数组{ int k=0; for(int i=0;i<G.vexnum;++i) { for(int j=0;j<G.vexnum;++j) { if( (G.arcs[i][j] != MAX) && (i<j)) { edges[k].begin = i; edges[k].end=j; edges[k].weight=G.arcs[i][j]; k++; } } } k=G.edgenum-1; for(int i=0;i<G.edgenum;++i) //讲转换的边集数组按从小到大的顺序排列 { int weight=0,index=0; for(int j= 0;j<k;++j) { if(edges[j].weight>weight) { weight = edges[j].weight; index = j; } } sort_edges(edges[index],edges[k]); --k; } }int find(int* parent,int num) //查找已添加进树中的顶点最后尾部索引{ while(parent[num]) num = parent[num]; return num;}void minTree_kruskal(MGraph &G){ int *parent = (int*)malloc(sizeof(int)*G.vexnum); Edge *edges = (Edge*)malloc(sizeof(Edge)*G.edgenum); turn_to_edges(G,edges); for(int i=0;i<G.vexnum;++i) parent[i]=0; for(int i=0;i<G.edgenum;++i) { int n = find(parent,edges[i].begin); int m = find(parent,edges[i].end); if(m!=n) //若相等,则添加此边会形成环 { parent[n]=m; printf("(%d,%d) %d",edges[i].begin,edges[i].end,edges[i].weight); } }}
以上只是粗略的完成两个算法的思想,有很多不严谨的地方,但本文重心只是两种经典最小生成树的原理。