最小生成树:这里面有两个概念:(1):必须为一个树,并且为一棵生成树(树的定义有且仅有一个前驱结点,可以有有多个后驱子节点,并且(n-1)条边都在图中)
(2):必须是最小连通的。(多一条边会使树形成回路,不满足生成树的概念,少一条边,则会使树不连通)
因此,最小生成树必须满足三个条件:
(1):必须包含n个结点
(2):必须包含(n-1) 条边
(3):不能构成回路。
最小生成树的两种构造方法:Prim算法,Kruskal算法。
1:Prim算法生成最小生成树
prim 算法的伪码描述:适合于稠密图
1 void prim(){ 2 MST = {s}; 3 while(1){ 4 v=未收录顶点中最小dist的结点; 5 if(v不存在) 6 break; //跳出循环中; 7 8 if(v已经收录),dist[v]=0; 9 for(v的每一个邻接点w){ 10 if(w没有被收录){ // if(dist[w]!=0) 11 if(E(v,w)< dist[w]){ 12 dist[w]= E(v,w); 13 parent[w]=v; 14 } 15 } 16 if(MST中的v不够n个){ 17 不能形成最小生成树 18 } 19 20 } 21 } 22 }
prim算法的代码描述:
1 package com.hone.graph.MinCreateTree; 2 import com.hone.graph.AdjMWGraph; 3 4 //用prim算法建立带权图g的最小生成树closeVertex 5 public class Prim { 6 static final int maxweight=9999; //设置初始化权值 7 public static void prim(AdjMWGraph g, MinSpanTree[] closeVertex ) throws Exception{ 8 int n=g.getNumOfVertices(); //获得结点的个数 9 int minCost; 10 int[] lowCost= new int[n]; //用数组来存储最小权值边(u,v) 11 int k=0; 12 13 for (int i = 1; i < n; i++) { 14 lowCost[i]=g.getWeight(0, i); //lowCost的初始值 15 } 16 17 MinSpanTree temp=new MinSpanTree(); 18 19 //从结点0出发构造最小生成树 20 temp.vertex=g.getValue(0); 21 closeVertex[0]=temp; //保存结点0 22 lowCost[0]=-1; //用-1标记结点0,表示该结点已经加入到集合中 23 24 //寻找最小权值对应的结点k 25 for (int i = 1; i < n; i++) { 26 minCost=maxweight; //maxWeight为定义的最大权值 27 for (int j = 0; j < n ; j++) { 28 if (lowCost[j]<minCost&&lowCost[j]>0) { //获得权值更小的点 29 minCost=lowCost[j]; 30 k=j; 31 } 32 } 33 34 //开始构建最小生成树的当前结点和权值数据 35 MinSpanTree curr=new MinSpanTree(); 36 curr.vertex=g.getValue(k); //保存此次循环中最小权值的结点k 37 curr.weight=minCost; //保存对应的权值 38 closeVertex[i]=curr; 39 lowCost[k]=-1; //标记结点k,表示该结点已经收录到最小生成树中 40 41 42 //根据加入集合中u的结点修改lowCost中的数值 43 for(int j=1; j<n; j++ ){ 44 if (g.getWeight(k, j)<lowCost[j]) { 45 lowCost[j]=g.getWeight(k, j); 46 } 47 } 48 } 49 } 50 }
2:Kruskal算法(克鲁斯卡尔算法)
克鲁斯卡尔算法是按照带权图中边的权值的递增顺序构造最小生成树的方法。适合于稀疏图
因此克鲁斯卡尔算法主要包括两部分:(1):带权图G中各个边的权值排序(时间复杂度的主要因素)
(2):判断新选择的两个结点是否会构成一个回路(避免)
克鲁斯卡尔算法的伪码描述:
时间复杂度主要在于边权重结点之间的排序。O(eloge)