后端 绘制有向无环图(DAG图)

因为公司的某个系统比较古老,里面的 job 的管理几乎都是直接通过操作数据库来实现的,对于一般的操作还可以忍受,但是每次想知道 job 之间的依赖关系的时候,就会相当难受,于是便脑袋很抽地一定要写一个查询系统能快速查询 job 之间的依赖关系。

分析

在项目开始之前,大概稍微估算了一下难点,大概可以分为以下两点:

如何在前端显示该依赖图
如何提高查询效率

此篇文章只看第一个问题,由于是本人是前端渣,于是只能去网上搜相应图形显示插件。

前端插件选择

找了很久,终于在 echarts 上找到了一个稍微适合自己的图形插件了,

http://echarts.baidu.com/examples/editor.html?c=graph-simple

不过,这个插件只是一个简单图的插件,也就是说这个插件并不会帮你进行自动定位相应的点,我们需要自己确定各个点的坐标。

解决方案

通过观察发现,该插件上的坐标只需要确定相对坐标就行了,它会根据相对位置进行自动定位,于是感觉瞬间轻松了许多。

仔细思考,发现 job 之间的依赖关系是一个有向无环图,于是开始构思。

要求:

将点按层次显示,保证前面层的点只依赖后面层的点 (同一层也禁止存在依赖关系)。
使得图尽可能整洁,尽量减少线段之间的交叉

解决方案:

通过拓扑排序来实现点的层次选择,每次将所有入度为 0 的点分为同一层即可
通过与前面层的点之间的关系设置相应的权重,同层之间根据权重来排序。我记得好像可以通过动态规划得到最优解的,不过实现起来太麻烦了,就算了

代码如下:

import java.util.*;


 * 作用: 绘制有向无环图(DAG图)
 *
 * 说明:
 * 配合Echarts的simple graph使用,地址见http://echarts.baidu.com/examples/editor.html?c=graph-simple
 * 用来进行点的坐标定位
 *
 * 使用条件: 图必须是一个有向无环图(DAG图)
 *
 * 调用方式: return new DagGraphUtil(points, links).drawDagGraph()
 *
 * 参数:
 * points的格式为 ['点1','点2','点3','点4']
 * links的格式为 [{"source":"点1","target":"点2"},{"source":"点2","target":"点3"},{"source":"点3","target":"点4"}]
 *
 * 返回结果:
 * {
 *     "points": [{"name": "点1","x":100,"y":100},{"name":"点2","x":200,"y":200}, ...],
 *     "links": [{"source":"点1","target":"点2"},{"source":"点2","target":"点3"}, ...]
 * }
 *
 *
 * 主要算法: 拓扑排序
 */
public class  {
    private final int MAX_LENGTH_X = 900;
    private final int MAX_LENGTH_Y = 1000;
    private int maxX;
    private Map toNum;
    private Map xPosition;
    private Map yPosition;
    private Map pointWeight;
    private List ySize;
    private List points;
    private List> links;

    public (List points, List> links) {
        this.points = points;
        this.links = links;
        toNum = new HashMap<>(points.size());
        xPosition = new HashMap<>(points.size());
        yPosition = new HashMap<>(points.size());
        pointWeight = new HashMap<>(points.size());
        maxX = 0;
        ySize = new ArrayList<>();

        for (String point : points) {
            toNum.put(point, 0);
            pointWeight.put(point, 0);
        }
        for (Map link : links) {
            String toPoint = link.get("target");
            addMapValue(toNum, toPoint, 1);
        }
    }

    public Map drawDagGraph() {
        Map result = new HashMap<>(3);
        dfs(0);
        List> pList = new ArrayList<>(points.size());
        for (String point : points) {
            int xUnitLength = MAX_LENGTH_X / maxX;
            int x = xPosition.get(point) * xUnitLength + xUnitLength / 2;
            int yUnitLength = MAX_LENGTH_Y / ySize.get(xPosition.get(point));
            int y = yPosition.get(point) * yUnitLength + yUnitLength / 2;
            Map map = new HashMap<>(3);
            map.put("name", point);
            map.put("x", x);
            map.put("y", y);
            pList.add(map);
        }
        result.put("points", pList);
        result.put("links", links);
        return result;
    }

    private void dfs(int depth) {
        List currentPoints = new ArrayList<>();
        for (String key : toNum.keySet()) {
            if (toNum.get(key) == 0) {
                currentPoints.add(key);
                addMapValue(toNum, key, -1);
            }
        }
        if (currentPoints.isEmpty()) {
            return;
        }
        maxX = depth + 1;
        ySize.add(currentPoints.size());
        Collections.sort(currentPoints, new Comparator() {
            
            public int compare(String o1, String o2) {
                return pointWeight.get(o1) - pointWeight.get(o2);
            }
        });
        for (int i = 0, len = currentPoints.size(); i < len; i++) {
            String point = currentPoints.get(i);
            xPosition.put(point, depth);
            yPosition.put(point, i);
            for (Map link : links) {
                if (link.get("source").equals(point)) {
                    maxMapValue(pointWeight, link.get("target"), i + 1);
                    addMapValue(toNum, link.get("target"), -1);
                }
            }
        }
        dfs(depth + 1);
    }

    private void addMapValue(Map map, String key, Integer value) {
        if (map.containsKey(key)) {
            map.put(key, map.get(key) + value);
        } else {
            map.put(key, value);
        }
    }

    private void maxMapValue(Map map, String key, Integer value) {
        if (!map.containsKey(key) || map.get(key) == 0 || map.get(key) < value) {
            map.put(key, value);
        }
    }
}

你可能感兴趣的:(总结,数据血缘,java,DAG)