zoukankan      html  css  js  c++  java
  • 最小生成树:Kruskal算法 和 Prim算法(第23章)

    武侠: 飞雪连天射白鹿,笑书神侠倚碧鸳。 ——金庸十四著作
    飞狐外传 、雪山飞狐 、连城诀 、天龙八部 、射雕英雄传 、白马啸西风 、鹿鼎记 、笑傲江湖 、书剑恩仇录 、神雕侠侣 、侠客岛 、倚天屠龙记 、碧血剑 、鸳鸯刀 (除此之外还缺少越女剑)。

    声明:本文参考了华山大师兄博客最小生成树-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;
    
        }
    
    }
    
    踏实 踏踏实实~
  • 相关阅读:
    js 前端开发 编程 常见知识点笔记
    重置 PowerShell 和 cmd 设置 样式 为系统默认值 powershell windows10
    useMemo和useCallback的区别 及使用场景
    数组去重,利用 ES6 的 reduce() 方法 和 include 判断 实现
    Java 中 Lombok 的使用,提高开发速度必备
    记录 windows 系统常用的 CMD 命令
    React Native 的 FlatList 组件 实现每次滑动一整项(item)
    Spring------mysql读写分离
    Webservice与CXF框架快速入门
    quartz
  • 原文地址:https://www.cnblogs.com/mrzhang123/p/5365802.html
Copyright © 2011-2022 走看看