最小生成树:Kruskal算法 和 Prim算法(第23章)
2016-03-17 21:22
239 查看
武侠: 飞雪连天射白鹿,笑书神侠倚碧鸳。 ——金庸十四著作
飞狐外传 、雪山飞狐 、连城诀 、天龙八部 、射雕英雄传 、白马啸西风 、鹿鼎记 、笑傲江湖 、书剑恩仇录 、神雕侠侣 、侠客岛 、倚天屠龙记 、碧血剑 、鸳鸯刀 (除此之外还缺少越女剑)。
声明:本文参考了华山大师兄博客最小生成树-Prim算法和Kruskal算法。结合自己学习《算法导论》的认识形成的笔记。感谢网友的总结分享。
1).记Graph中有v个顶点,e个边。
2).新建图Graphnew,Graphnew中拥有原图中相同的v个顶点,但没有边。
3).将原图Graph中所有e个边按权值从小到大排序
4).循环:从权值最小的边开始遍历每条边 直至图Graph中所有的节点都在同一个连通分量中
if 这条边连接的两个节点于图Graphnew中不在同一个连通分量中
添加这条边到图Graphnew中
1).输入:一个加权连通图,其中顶点集合为V,边集合为E;
2).初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {},为空;
3).重复下列操作,直到Vnew = V:
a.在集合E中选取权值最小的边
飞狐外传 、雪山飞狐 、连城诀 、天龙八部 、射雕英雄传 、白马啸西风 、鹿鼎记 、笑傲江湖 、书剑恩仇录 、神雕侠侣 、侠客岛 、倚天屠龙记 、碧血剑 、鸳鸯刀 (除此之外还缺少越女剑)。
声明:本文参考了华山大师兄博客最小生成树-Prim算法和Kruskal算法。结合自己学习《算法导论》的认识形成的笔记。感谢网友的总结分享。
1. 最小生成树的生成
一个带权的无向连通图,如何选取一棵生成树,使树上所有边上权的总和为最小,这叫最小生成树。Kruskal算法 和 Prim算法都是使用贪心策略来解决最小生成树的问题。//无相连通图G(V,E)和权重函数w:E->R. Generic-MST(G,w){ A=Φ;//Φ表示空集 while(A does not from a spinning tree){ find a edge(u,v) that is safe for A; A=A∪{(u,v)} } return A; }
2. 克鲁斯卡尔算法
算法思想:遍历根据权重从小到大排序的边,依据约束,合并森林成为目标树。(自己理解的大白话)1).记Graph中有v个顶点,e个边。
2).新建图Graphnew,Graphnew中拥有原图中相同的v个顶点,但没有边。
3).将原图Graph中所有e个边按权值从小到大排序
4).循环:从权值最小的边开始遍历每条边 直至图Graph中所有的节点都在同一个连通分量中
if 这条边连接的两个节点于图Graphnew中不在同一个连通分量中
添加这条边到图Graphnew中
3. 普里姆算法
算法思想:从一点出发,先添加连通的最近的结点,一直到所有的几点都包含在目标树中。(自己理解的大白话)1).输入:一个加权连通图,其中顶点集合为V,边集合为E;
2).初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {},为空;
3).重复下列操作,直到Vnew = V:
a.在集合E中选取权值最小的边
4. 算法Java实现**
4.1 图的存储和表示
package lbz.ch23.mst; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * @author LbZhang * @version 创建时间:2016年3月16日 下午9:11:24 * @description 图 */ public class Graph { public final static int NODECOUNT = 9; public final static String[] VERTEXS={"a","b","c","d","e","f","g","h","i"}; private final int[][] EDGEVALUE = { { 0, 4, 0, 0, 0, 0 , 0 ,8, 0}, { 4, 0, 8, 0, 0, 0 , 0 ,11, 0}, { 0, 8, 0, 7, 0, 4 , 0 ,0, 2}, { 0, 0, 7, 0, 9, 14, 0 ,0, 0}, { 0, 0, 0, 9, 0, 10, 0 ,0, 0}, { 0, 0, 4, 14, 10, 0, 2 ,0, 0}, { 0, 0, 0, 0, 0, 2, 0 ,1, 6}, { 8, 11, 0, 0, 0, 0, 1 ,0, 7}, { 0, 0, 2, 0, 0, 0, 6 ,7, 0} }; public int count; public String[] vertexstr; public int[][] edgesValue; public Graph() { this.count=NODECOUNT; this.edgesValue=EDGEVALUE; this.vertexstr=VERTEXS; } /** * 获取当前图中的所有的边集合 * @return */ public List<FromTo> getSortedEdgeFromTo(){ List<FromTo> fts = new ArrayList<FromTo>(); for(int i=0;i<NODECOUNT;i++){ for(int j=0;j<=i;j++){ if(this.edgesValue[i][j]!=0){ FromTo ft = new FromTo(); ft.setFrom(new TreeNode(this.vertexstr[i])); ft.setTo(new TreeNode(this.vertexstr[j])); ft.setWeight(this.edgesValue[i][j]); fts.add(ft); } } } ComparatorFromTo cft = new ComparatorFromTo(); Collections.sort(fts, cft); // for(int i=0;i<fts.size();i++){ // System.out.print(fts.get(i).weight+" "); // } // System.out.println(); return fts; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } public String[] getVertexstr() { return vertexstr; } public void setVertexstr(String[] vertexstr) { this.vertexstr = vertexstr; } public int[][] getEdgesValue() { return edgesValue; } public void setEdgesValue(int[][] edgesValue) { this.edgesValue = edgesValue; } /** * 图的链接矩阵表示的输出打印 */ public void printMGraph() { System.out.print(" ");//这里的间隙使用的是tab 制表键 for(int i=0;i<this.count;i++){ System.out.print(this.vertexstr[i] + " "); } System.out.println(); for (int i = 0; i < this.edgesValue[0].length; i++) { System.out.print(this.vertexstr[i] + " "); for (int j = 0; j < this.edgesValue[0].length; j++) { System.out.print(this.edgesValue[i][j] + " "); } System.out.println(); } System.out.println("------------图的链接矩阵表示的输出打印结束------------"); } /** * 类内测试函数 * @param args */ public static void main(String[] args) { Graph g = new Graph(); g.printMGraph(); } }
4.2 树的存储和表示
package lbz.ch23.mst; import java.util.Comparator; import sun.misc.Compare; /** * @author LbZhang * @version 创建时间:2016年3月17日 上午5:41:52 * @description 边类 */ public class FromTo{ public int weight; public TreeNode from; public TreeNode to; @Override public String toString() { return this.from.getVetex()+"->"+this.getTo().getVetex()+":"+this.weight+""; } public FromTo() { super(); } public FromTo(int weight, TreeNode from, TreeNode to) { super(); this.weight = weight; this.from = from; this.to = to; } public int getWeight() { return weight; } public void setWeight(int weight) { this.weight = weight; } public TreeNode getFrom() { return from; } public void setFrom(TreeNode from) { this.from = from; } public TreeNode getTo() { return to; } public void setTo(TreeNode to) { this.to = to; } }
package lbz.ch23.mst; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; /** * @author LbZhang * @version 创建时间:2016年3月17日 下午3:48:34 * @description 比较类 */ public class ComparatorFromTo implements Comparator{ @Override public int compare(Object o1, Object o2) { FromTo ft1 = (FromTo)o1; FromTo ft2 = (FromTo)o2; int flag = 0; if(ft1.getWeight()>=ft2.getWeight()){ flag=1; }else{ flag=-1; } return flag; } @SuppressWarnings("unchecked") public static void main(String[] args) { System.out.println("Test !@"); List<FromTo> fts = new ArrayList<FromTo>(); fts.add(new FromTo(3,new TreeNode("A"),new TreeNode("B"))); fts.add(new FromTo(1,new TreeNode("A"),new TreeNode("B"))); fts.add(new FromTo(5,new TreeNode("A"),new TreeNode("B"))); fts.add(new FromTo(2,new TreeNode("A"),new TreeNode("B"))); ComparatorFromTo cft = new ComparatorFromTo(); Collections.sort(fts, cft); for(int i=0;i<fts.size();i++){ System.out.println(fts.get(i).weight); } } }
package lbz.ch23.mst; import java.util.ArrayList; import java.util.List; /** * @author LbZhang * @version 创建时间:2016年3月17日 上午5:14:25 * @description 树结点类 */ public class TreeNode { public String vetex; public List<FromTo> from = new ArrayList<FromTo>(); public List<FromTo> to = new ArrayList<FromTo>(); public TreeNode(String vetex) { super(); this.vetex = vetex; } public TreeNode(String vetex, List<FromTo> from, List<FromTo> to) { super(); this.vetex = vetex; this.from = from; this.to = to; } public TreeNode() { super(); } public String getVetex() { return vetex; } public void setVetex(String vetex) { this.vetex = vetex; } public List<FromTo> getFrom() { return from; } public void setFrom(List<FromTo> from) { this.from = from; } public List<FromTo> getTo() { return to; } public void setTo(List<FromTo> to) { this.to = to; } }
package lbz.ch23.mst; import java.util.ArrayList; import java.util.List; /** * @author LbZhang * @version 创建时间:2016年3月17日 下午2:56:36 * @description 最小生成树结构类 */ public class MSTree { public TreeNode root;//树的根节点 public List<TreeNode> tns = new ArrayList<TreeNode>(); public List<FromTo> fts=new ArrayList<FromTo>(); public MSTree() { super(); } public MSTree(TreeNode root) { super(); this.root = root; this.tns.add(root); } public MSTree(TreeNode r, List<TreeNode> tns, List<FromTo> fts) { super(); this.root=r; this.tns = tns; this.fts = fts; } public TreeNode getRoot() { return root; } public void setRoot(TreeNode root) { this.root = root; } public List<TreeNode> getTns() { return tns; } public void setTns(List<TreeNode> tns) { this.tns = tns; } public List<FromTo> getFts() { return fts; } public void setFts(List<FromTo> fts) { this.fts = fts; } /** * 判断两个顶点是否在当前的最小生成树中 * @param from * @param to * @return */ public boolean containsTwoNodes(TreeNode from, TreeNode to) { boolean flag = false; int count = 0; // System.out.println(tns.size()); // for(int i=0;i<tns.size();i++){ // System.out.print(":"+tns.get(i).getVetex()); // } // System.out.println(); for(int i=0;i<tns.size();i++){ //判定如果在一个最小生成树中有当前的需要比对的节点count+1 if((tns.get(i).getVetex().equals(from.getVetex()))||(tns.get(i).getVetex().equals(to.getVetex()))){ count++; } if(count>=2){ flag=true; break; } } return flag; } /** * 当前树中包含当前结点 * @param from * @return */ public boolean containsNode(TreeNode tn) { boolean flag = false; for(int i=0;i<tns.size();i++){ if((tns.get(i).getVetex().equals(tn.getVetex()))){ flag=true; break; } } return flag; } }
4.3 克鲁斯卡尔(Krusckal)算法的实现
package lbz.ch23.mst; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; /** * @author LbZhang * @version 创建时间:2016年3月16日 下午9:09:05 * @description MST Kruskal算法实现类 * 无向连通图——最小生成树算法的设计实现和测试 * * MinimumSpanningTree */ public class KruskalMST { public static void main(String[] args) { System.out.println("My Kruskal MST TEST! "); Graph g = new Graph(); g.printMGraph(); System.out.println("----无向连通图构建完毕----"); //克鲁斯卡尔算法的最小生成树 Set<FromTo> frset = MstKruskal(g); Iterator<FromTo> it = frset.iterator(); System.out.println("----最小生成树的结果---"); while(it.hasNext()){ FromTo ft = it.next(); System.out.println(ft.getFrom().getVetex()+"->"+ft.getTo().getVetex()+": "+ ft.getWeight()); } } /** * Kruskal 算法的核心内容的实现 * @param g * @return */ private static Set<FromTo> MstKruskal(Graph g) { Set<FromTo> FRSet = new HashSet<FromTo>(); //最小生成树列表 MSTree mst = new MSTree(); int n=g.count;//结点的个数 List<MSTree> mstList = new ArrayList<MSTree>(); //初始化各个最小生成树 for(int i=0;i<n;i++){ MSTree mt = new MSTree(new TreeNode(g.getVertexstr()[i])); mstList.add(mt); } //根据权重排序 List<FromTo> fts = g.getSortedEdgeFromTo(); for(int i=0;i<fts.size();i++){ System.out.println(fts.get(i).weight+" "+fts.get(i).getFrom().getVetex()+"->"+fts.get(i).getTo().getVetex()); } System.out.println("----根据权重排序 一共有:"+fts.size()+"条边----"); for(int i=0;i<fts.size();i++){ FromTo ft = fts.get(i); if(ft.getWeight()==8){ System.out.println(); } if(!findInSameTree(ft.from,ft.to,mstList)){ FRSet.add(ft); unionRelatedTree(ft.from,ft.to,ft,mstList); } } return FRSet; } /** * 将通过边连接起来的两个最小生成树合并 * @param from * @param to * @param ft * @param mstList */ private static void unionRelatedTree(TreeNode from, TreeNode to, FromTo ft, List<MSTree> mstList) { MSTree mst1 = new MSTree(),mst2=new MSTree(); int memo = 0; for(int i=0;i<mstList.size();i++){ MSTree mst = mstList.get(i); if(mst.containsNode(from)){ mst1=mst; }else if(mst.containsNode(to)){ mst2=mst; memo=i; } } //将两个链表合并为一个 mst1.getFts().addAll(mst2.getFts()); mst1.getTns().addAll(mst2.getTns()); mstList.remove(memo); } /** * 判断当前的边的两个端点是否位于同一最小生成树中 * @param from * @param to * @param mstList * @return */ private static boolean findInSameTree(TreeNode from, TreeNode to, List<MSTree> mstList) { boolean flag = false; for(int i=0;i<mstList.size();i++){ MSTree mst = mstList.get(i); if(mst.containsTwoNodes(from,to)){ flag=true; break; } } return flag; } }
4.4 普利姆(Prim)算法的实现
package lbz.ch23.mst; import java.util.Iterator; import java.util.List; import java.util.Set; /** * @author LbZhang * @version 创建时间:2016年3月17日 下午8:21:45 * @description MST Prim算法实现类 * 无向连通图——最小生成树算法的设计实现和测试 */ public class PrimMST { public static void main(String[] args) { System.out.println("My Prim MST TEST! "); Graph g = new Graph(); g.printMGraph(); System.out.println("----无向连通图构建完毕----"); MSTree mst = new MSTree(); TreeNode root = new TreeNode("a"); mst=MstPrim(g,root); System.out.println("输出Prim构造的最小生成树的结果演示"); for(int i=0;i<mst.getFts().size();i++){ System.out.println(mst.getFts().get(i)); } System.out.println(); } private static MSTree MstPrim(Graph g, TreeNode root) { MSTree mst = new MSTree(root); //获取当前图中的边从小到大的集合 List<FromTo> fts = g.getSortedEdgeFromTo(); while(mst.getTns().size()<=g.getCount()){ FromTo ft = extractMin(fts,mst);// if(ft==null){//最后一个会传出null的值 break; } mst.getTns().add(ft.to); mst.getFts().add(ft); } return mst; } private static FromTo extractMin(List<FromTo> fts, MSTree mst) { FromTo ft=null; TreeNode tnv = null; for(int i=0;i<fts.size();i++){ ft = fts.get(i); if(mst.containsNode(ft.from)&&!mst.containsNode(ft.to)){ tnv=ft.to; ft.to=ft.from; ft.from=tnv; fts.remove(i); break; }else if(!mst.containsNode(ft.from)&&mst.containsNode(ft.to)){ tnv=ft.from; ft.from=ft.to; ft.to=tnv; fts.remove(i); break; }else{ ft=null; } } return ft; } }
相关文章推荐
- 我的第一个网页
- banner图片自适应
- Web2.0 TA 问题记录
- FZU 1063 三维扫描
- 电路与电子学-第一章直流电路分析方法小概括
- 毕业设计第三十天
- Bzoj1875: [SDOI2009]HH去散步:邻接矩阵的幂
- 为大家提供一个完整的Hibernate Annotation项目_源码下载
- 为什么会存在TIME_WAIT < 初讲>
- java工作错误集
- Spring AOP(三)——通过@AspectJ注解切面
- MPI和MIC
- FZU 1062 洗牌问题
- linux基础命令(6)
- FZU 1063 三维扫描
- C# kinect v2学习笔记(一) kinect V2配置安装
- 常用sql语句(基础)
- Python学习笔记day9
- Spinner的功能和用法
- hdu2126(01背包)