典型的最小生成树算法,题目给出图的顶点以及所有边的权值要求输出最小生成树对应的边,我分别用prim算法和Kruskal算法实现,结果prim算法47ms,4364K ,Kruskal算法204ms,4148K,下面分别详细讲述:
对于图,其生成树中的边也带权,将生成树各边的权值总和称为生成树的权,并将权值最小的生成树称为最小生成树(Minimun Spanning Tree),简称为MST。有两种非常典型的算法:Prim算法和kruskal算法,这两种算法都采用了贪心策略。
1. prim算法:
Prim算法的基本思想是:
(1) 在图G=(V,E)(V表示顶点,E表示边)中,从集合V中任取一个顶点(例如取顶点v0)放入集合U中,这时U={v0},集合T(E)为空。
(2) 从v0出发寻找与U中顶点相邻(另一顶点在V中)权值最小的边的另一顶点v1,并使v1加入U。即U={v0,v1},同时将该边加入集合T(E)中。
(3) 重复(2),直到U = V为止。
这时T(E)中有n-1条边,T=(U,T(E))就是一棵最小生成树。
在本例中,数组origin存放原始数据,max_distance存放矩阵中的最大值,
result存放最小生成树的最大边,opt存放节点和最小生成树之间的最小距
离,line[i]存放节点i和生成树连接的另一节点,flag判断是否已经加入到最
小生成树中,首先将1号顶点加入最小生成树中,flag[1]为true,其他为false,
opt[i]的值为origin[1][i]的值 ,line[i]全为1 ,然后选择不在最小生成树
中的最小边i,然后加入到最小生成树中,另外更新opt[i],flag[i],line[i],
如此反复,直到取到v-1条边为止。
带有详细注释的代码可以从http://download.csdn.net/user/china8848/获得。
2. 并查集+Kruskal算法:
并查集最经典的应用其实就是Kruskal算法,在算法导论21章:用于不相交的集合有详细讲述。这里使用了路径压缩,按秩合并的思想,详见算法导论。
Kruskal算法每次选择n-1条边,所使用的贪婪准则是:从剩下的边中选择一条不会产生环路的具有最小耗费的边加入已选择的边的集合中。注意到所选取的边若产生环路则不可能形成一棵生成树。Kruskal算法分e步,其中e是图中边的数目。按耗费递增的顺序来考虑这e条边,每次考虑一条边。当考虑某条边时,若将其加入到已选边的集合中会出现环路,则将其抛弃,否则,将它选入。
在本例中,flag[i]存放第i条边是否还有必要考虑,如果该边会产生回路或者已经在最小生成树中就不考虑了,start[],end[],weight[]分别存放图中边的起点,终点和权值,对每个顶点初始化,每个顶点单独成为一个集合,然后将所有边按权值排序,取最小的边,该边如果不在同一个集合中,合并,并将结果存入start_s&start_e中,并记录当前加入最小生成树的最大边权值,边数count++,并置该边为不考虑。
值得一提的是,该实现中对边按权值的排序采用了计数排序,其时间复杂度为O(n)。
带有详细注释的代码可以从http://download.csdn.net/user/china8848/获得。