zoukankan      html  css  js  c++  java
  • 图的最小生成树(java实现)

    1.图的最小生成树(贪心算法)

    我两个算法的输出都是数组表示的,当前的索引值和当前索引对应的数据就是通路,比如parent[2] = 5;即2和5之间有一个通路,第二个可能比较好理解,第一个有点混乱

    是什么?

    将一个有权图中的 所有顶点 都连接起来,并保证连接的边的 总权重最小,即最小生成树,最小生成树不唯一

    为什么?

    传入邻接矩阵,返回可以生成最小生成树的数据

    我们有两种方式生成图的最小生成树1.普里姆(Prim)算法2.克鲁斯卡尔(Kruskal)算法

    怎样做?

    图片参考博客:https://blog.csdn.net/afei__/article/details/83316587

    下面是普里姆算法的最小生成树

    下面是克鲁斯卡尔算法的最小生成树:

    图的邻接矩阵表示法(无向图,上三角矩阵)

    int[][] arr = new int[][]{
    {-1, 4, 0, 0, 0, 0, 0, 8, 0},
    {0, -1, 8, 0, 0, 0, 0, 11, 0},
    {0, 0, -1, 7, 0, 4, 0, 0, 2},
    {0, 0, 0, -1, 9, 14, 0, 0, 0},
    {0, 0, 0, 0, -1, 10, 0, 0, 0},
    {0, 0, 0, 0, 0, -1, 2, 0, 0},
    {0, 0, 0, 0, 0, 0, -1, 1, 6},
    {0, 0, 0, 0, 0, 0, 0, -1, 7},
    {0, 0, 0, 0, 0, 0, 0, 0, -1}
    };

    1.普里姆算法(加点法)

    需求:求出最小生成树的权值

    输入参数:二维数组arr(邻接矩阵),列表list(存放已经被加入的点),整型sum(存放权值)

    输出参数:整型数组parent

    1)先找一个起点,这个起点为任意一点,放入list中
    
    2)如果list中不包含全部节点,进入循环
    
      1>遍历list中节点,查找不存在list中的邻接节点的最小值,记录下begin和end
    
      2>将begin和end放入数组中,较小值节点赋值给较大值所在数组位置
    3)返回parent

     实现:

    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    /**
     * 普里姆(Prim)算法
     *
     * @author Xiong YuSong
     * 2019/3/22 16:02
     */
    public class Prim {
    
        public static void main(String[] args) {
            int[][] arr = new int[][]{
                    {-1, 4, 0, 0, 0, 0, 0, 8, 0},
                    {0, -1, 8, 0, 0, 0, 0, 11, 0},
                    {0, 0, -1, 7, 0, 4, 0, 0, 2},
                    {0, 0, 0, -1, 9, 14, 0, 0, 0},
                    {0, 0, 0, 0, -1, 10, 0, 0, 0},
                    {0, 0, 0, 0, 0, -1, 2, 0, 0},
                    {0, 0, 0, 0, 0, 0, -1, 1, 6},
                    {0, 0, 0, 0, 0, 0, 0, -1, 7},
                    {0, 0, 0, 0, 0, 0, 0, 0, -1}
            };
            List<Integer> list = new ArrayList<>();
            //先将0放置在list中
            list.add(0);
            int begin = 0, end = 0, weight;
            int[] parent = new int[arr.length];
            for (int i = 0; i < arr.length; i++) {
                parent[i] = -1;
            }
            while (list.size() < arr.length) {
                weight = Integer.MAX_VALUE;
                for (Integer row : list) {
                    for (int i = 0; i < arr.length; i++) {
                        if (!list.contains(i)) {
                            if (i >= row + 1) {
                                if (arr[row][i] > 0 && arr[row][i] < weight) {
                                    begin = row;
                                    end = i;
                                    weight = arr[row][i];
                                }
                            } else if (i <= row - 1) {
                                //我这里只用了上三角矩阵,所以这里需要画蛇添足写这一部分
                                if (arr[i][row] > 0 && arr[i][row] < weight) {
                                    begin = row;
                                    end = i;
                                    weight = arr[i][row];
                                }
                            }
                        }
                    }
                }
                list.add(end);
                parent[end] = begin;
            }
            System.out.println(Arrays.toString(parent));
        }
    }

    2.克鲁斯卡尔算法(加边法)

    需求:求出最小生成树的权值

    构建类:Edge<begin,end,weight>三元组,根据weight(权值)排序

    输入参数:存放有Edge的列表list,并查集parent

    输出参数:并查集parent(最小生成树的数组表现形式)

    原理:贪心算法的实现,程序中使用了并查集(判断两个集合中是否存在相同的数据)这种特殊的数据结构,使用数组实现

    1)创建一个三元组<起始点,终止点,权值>,将邻接矩阵中数据放入三元组中,再放入list中,根据权值进行排序
    
    2)创建变量count=0,整型数组parent
    
    3)如果list中还存在值,则进行循环
    
      1>判断begin和end是否存在于不同的集合中(判断是否在同一棵树中,即判断当前节点在并查集parent中的根节点是否为同一个)
    
      2>如果存在不同的集合中,则将较小值节点赋值给较大值所在数组位置,较小值节点为较大值节点的父节点
    
    4)返回parent

    实现:

    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.List;
    
    /**
     * @author Xiong YuSong
     * 2019/3/22 17:04
     */
    class Edge implements Comparable<Edge> {
        //起始点
        private int begin;
        //终止点
        private int end;
        //权值
        private int weight;
    
        public Edge(int begin, int end, int weight) {
            this.begin = begin;
            this.end = end;
            this.weight = weight;
        }
    
        public int getBegin() {
            return begin;
        }
    
        public void setBegin(int begin) {
            this.begin = begin;
        }
    
        public int getEnd() {
            return end;
        }
    
        public void setEnd(int end) {
            this.end = end;
        }
    
        public int getWeight() {
            return weight;
        }
    
        public void setWeight(int weight) {
            this.weight = weight;
        }
    
        @Override
        public int compareTo(Edge o) {
            if (o.weight > this.weight) {
                return -1;
            } else {
                return 1;
            }
        }
    }
    
    public class Kruskal {
    
        public static void main(String[] args) {
            //默认以a为根节点的最小生成树
            List<Edge> list = new ArrayList<>();
            int[][] arr = new int[][]{
                    {-1, 4, 0, 0, 0, 0, 0, 8, 0},
                    {0, -1, 8, 0, 0, 0, 0, 11, 0},
                    {0, 0, -1, 7, 0, 4, 0, 0, 2},
                    {0, 0, 0, -1, 9, 14, 0, 0, 0},
                    {0, 0, 0, 0, -1, 10, 0, 0, 0},
                    {0, 0, 0, 0, 0, -1, 2, 0, 0},
                    {0, 0, 0, 0, 0, 0, -1, 1, 6},
                    {0, 0, 0, 0, 0, 0, 0, -1, 7},
                    {0, 0, 0, 0, 0, 0, 0, 0, -1}
            };
            for (int i = 0; i < arr.length; i++) {
                for (int j = i + 1; j < arr.length; j++) {
                    if (arr[i][j] > 0) {
                        list.add(new Edge(i, j, arr[i][j]));
                    }
                }
            }
            Collections.sort(list);
            //数组中每一个节点都只知道他的父节点是什么,-1表示不存在父节点,0位置是根节点
            int[] parent = new int[arr.length];
            for (int i = 1; i < arr.length; i++) {
                parent[i] = -1;
            }
            int m = 0, n = 0;
            for (Edge edge : list) {
                //寻找这两个点有没有相同的父节点
                m = find(parent, edge.getBegin());
                n = find(parent, edge.getEnd());
                if (m != n && parent[edge.getEnd()]>0) {
                    parent[edge.getEnd()] = edge.getBegin();
                }
            }
            System.out.println(Arrays.toString(parent));
        }
    
        private static int find(int[] parent, int ch) {
            while (parent[ch] > 0) {
                ch = parent[ch];
            }
            return ch;
        }
    }
  • 相关阅读:
    关于工作中Git相关的总结
    浅谈MySQL的优化
    由内搜推送思考Kafka 的原理
    SOA和微服务架构
    Centos7.2 搭建Lamp服务器以及迁移WordPress个人博客详细过程
    MyISAM和InnoDB索引实现区别
    图解高内聚与低耦合
    图解Java常用数据结构(一)
    Java源码安全审查
    Java高并发之锁优化
  • 原文地址:https://www.cnblogs.com/xzmxddx/p/10581225.html
Copyright © 2011-2022 走看看