zoukankan      html  css  js  c++  java
  • java实现DAG广度优先便利

    在调度系统中经常可以看到有向无环图,假如我们用点表和边表来存储一个图,那么如何能实现按照优先级的排序呢?

    下面是我的一个实现:

    一、方案1

    子节点的优先级等于父节点的和?

    问题:虽然可以保证子节点一定大于等于父节点,但如果只有一个父节点,会导致父子优先级相同,因此否掉

    二、方案2

    子节点的优先级等于被指向的次数

    问题:被指向的次数多不代表优先级低,否掉

    三、方案3

    子节点优先级=父节点优先级的和,再加上被指向次数

    继承了方案1/2的优点,接下来看怎么实现?

    一开始想的是遍历边表,然后节点的优先级直接等于被指向次数+父节点优先级,发现有一个问题,父节点优先级后续有可能变化,但前面的子节点无法感知

    升级方案:

    1.先遍历边表,实现一个父节点的子节点map

    2.重新遍历边表,对被指向节点进行+1操作,之后递归遍历被指向节点的所有下游节点,统一进行+1操作,这样就实现了:

    在当前to节点+1时完成了被指向次数的初始化,

    递归子节点统一+1,完成了子节点=父节点优先级的求和操作。(上游节点自增时,下游都跟着自增,上游增多少,下游就增多少)

    public class GraphUtil {
        public static void main(String[] args) {
            Set<String> nodes = Sets.newHashSet("A", "B", "C", "D", "E", "F", "G");
            List<String> nodeList = Lists.newArrayList("G->E", "G->F", "E->D", "F->C","C->B", "C->A","B->A");
            List<Edge<String, String>> edges = nodeList.stream().map(str -> {
                String[] split = str.split("->");
                String from = split[0];
                String to = split[1];
                return new Edge<>(from, to);
            }).collect(Collectors.toList());
    
            Graph graph = new Graph(nodes, edges);
            Set<String> headNodes = findHeadNodes(graph);
            System.out.println(headNodes);
            Map<String, Integer> priority = findPriority(graph);
            System.out.println(priority);
        }
    
        /**
         * 解决方案1:
         * 通过计算被指向的次数,来计算节点的执行顺序
         * 问题:被指向的次数多,不一定优先级低!
         * 解决方案2:
         * 设置被指向节点优先级=所有父节点的和,保证子节点一定比父节点大
         * 问题:如果子节点只有一个父节点,子节点的优先级就会跟父节点相同!
         * 解决方案3:
         * 0.设置被指向节点优先级=所有父节点的和+被指向次数
         * 1.遍历边表,先生成一个父节点和子节点的映射map
         * 2.再次遍历边表,如果是头节点,默认值设0,如果是尾节点默认设0,已存在=节点原有值+1
         * 3.递归给尾节点的所有下游节点+1
         * 如此则避免了方案1、2的缺陷,保证子节点的优先级一定低于双亲节点
         *
         * @param graph
         * @return
         */
        public static Map<String, Integer> findPriority(Graph graph) {
            List<Edge<String, String>> edges = graph.getEdges();
            HashMultimap<String, String> childMap = HashMultimap.create();
            TreeMap<String, Integer> priorityMap = new TreeMap<>();
    
            for (Edge<String, String> s : edges) {
                String from = s.getStart();
                String to = s.getEnd();
                childMap.put(from, to);
            }
    
            System.out.println(childMap);
    
            for (Edge<String, String> s : edges) {
                String from = s.getStart();
                String to = s.getEnd();
                priorityMap.putIfAbsent(from, 0);
                priorityMap.putIfAbsent(to, 0);
                priorityMap.computeIfPresent(to, (k, v) -> v = v + 1);
                //不仅to自己更新,还要递归更新自己的下游节点
                updateChild(priorityMap, childMap, to);
            }
    
            return priorityMap;
        }
    
        /**
         * 只要某个节点+1,那么它后面的所有子节点都递归加1
         *
         * @param priorityMap
         * @param childMap
         * @param parent
         */
        public static void updateChild(TreeMap<String, Integer> priorityMap, HashMultimap<String, String> childMap, String parent) {
            if (childMap.containsKey(parent)) {
                Set<String> children = childMap.get(parent);
                for (String child : children) {
                    priorityMap.putIfAbsent(child, 0);
                    priorityMap.computeIfPresent(child, (k, v) -> v + 1);
                    if (childMap.containsKey(child)) {
                        updateChild(priorityMap, childMap, child);
                    }
                }
            }
        }
    
        /**
         * 查找头节点,可能不是一个
         *
         * @param graph
         * @return
         */
        public static Set<String> findHeadNodes(Graph graph) {
            List<Edge<String, String>> edges = graph.getEdges();
            Multimap<String, String> dependMap = HashMultimap.create();
            Map<String, Integer> priorityMap = new TreeMap<>();
            for (Edge<String, String> edge : edges) {
                String from = edge.getStart();
                String to = edge.getEnd();
                priorityMap.putIfAbsent(from, 0);
                priorityMap.putIfAbsent(to, 0);
                dependMap.put(to, from);
                priorityMap.computeIfPresent(to, (k, v) -> v = v + 1);
            }
    
            Set<String> set = Sets.newHashSet();
            for (Map.Entry<String, Integer> s : priorityMap.entrySet()) {
                if (s.getValue() == 0) {
                    set.add(s.getKey());
                }
            }
            return set;
        }
    
    
    }

    Graph类,Edge类

    /**
     * @Author : wangbin
     * @Date : 2021/9/28 14:36
     * @Description: 以边表和点表构成的有向无环图数据结构
     */
    @Data
    @AllArgsConstructor
    public class Graph {
        private Set<String> nodes;
        private List<Edge<String, String>> edges;
    }
    
    //======================================
    
    @Data
    @AllArgsConstructor
    public class Edge<S,E> {
        private S start;
        private E end;
    
    }

    输出:

    [G]
    {B=[A], C=[A, B], E=[D], F=[C], G=[E, F]}
    {A=7, B=3, C=2, D=2, E=1, F=1, G=0}

     补充:

    虽然得到了treeMap,但是它是以key进行排序,如果想按照value输出,还有进行处理
    /**
         * 获取以优先级顺序排序的<任务:优先级>list
         * @param graph
         * @return
         */
        public static List<Map.Entry<String, Integer>> getPriorityList(Graph graph) {
            TreeMap<String, Integer> priorityMap = findPriority(graph);
            List<Map.Entry<String, Integer>> entries = new ArrayList<>(priorityMap.entrySet());
            entries.sort(Map.Entry.comparingByValue());
            return entries;
        }
  • 相关阅读:
    C# JavascriptSerializer与匿名对象打造Json的完美工具
    C# 跨线程访问或者设置UI线程控件的方法
    使用Windows Live发布博客到博客园
    Ubuntu搭建ssh连接(连接方式:桥接网卡、网络地址转换(NAT))
    SQLServer right函数 从右侧截取指定位数的字符串
    python+MySQL架构
    pip换源(更换软件镜像源)
    Ubuntu搭建mysql,Navicat Premium连接
    一起学习造轮子(三):从零开始写一个React-Redux
    一起学习造轮子(二):从零开始写一个Redux
  • 原文地址:https://www.cnblogs.com/wangbin2188/p/15350184.html
Copyright © 2011-2022 走看看