算法->AOV网(拓扑排序)

311 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第27天,点击查看活动详情

AOV网

在现代化管理中,人们常用有向图来描述和分析一项工程的计划和实施过程,一个工程常被分为多个小的子工程,这些子工程被称为活动(Activity),在有向图中若以顶点表示活动,有向边表示活动之间的先后关系,这样的图简称为AOV网。

标准的AOV网必须是一个有向无环图(Directed Acyclic Graph,简称 DAG)

image.png

拓扑排序

image.png

前驱活动: 有向边起点的活动称为终点的前驱活动  (注意: 只有当一个活动的前驱全部都完成后,这个活动才能进行)
后继活动: 有向边终点的活动称为起点的后继活动

什么是拓扑排序

将AOV网中所有活动排成一个序列,使得每个活动的前驱活动都排在该活动的前面

比如上图的拓扑排序结果是:A、B、C、D、E、F 或者 A、B、D、C、E、F (结果并不一定是唯一的)

拓扑排序

可以使用卡恩算法(Kahn)完成拓扑排序
假设 L 是存放拓扑排序结果的列表
(1) 把所有入度为 0 的顶点放入 L 中,然后把这些顶点从图中去掉
(2) 重复操作(1),直到找不到入度为 0 的顶点

  • 如果此时 L 中的元素个数和顶点总数相同,说明拓扑排序完成
  • 如果此时 L 中的元素个数少于顶点总数,说明原图中存在环,无法进行拓扑排序

image.png

代码实现

/**
 * 拓扑排序(卡恩算法)
 */
@Override
public List<V> topologicalSort() {
    List<V> list = new ArrayList<>(); //存储排好序的顶点值
    Queue<Vertex<V, E>> queue = new LinkedList<>(); //存储度为0的顶点
    Map<Vertex<V, E>, Integer> ins = new HashMap<>(); //存储顶点与实时入度的映射
 
    /**
     * 将入度为 0的顶点入栈,作为起始顶点;
     * 将入度不为 0的顶点加入ins映射中, 后面进行实时更新
     */
    vertices.forEach((V v,Vertex<V,E> vertiex)->{
        int inSize = vertiex.inEdges.size();
        if(inSize == 0){
            queue.offer(vertiex);
        }else{
            ins.put(vertiex,inSize);
        }
    });
 
    while(!queue.isEmpty()){
        Vertex<V, E> vertex = queue.poll(); //队头元素出队
        list.add(vertex.value);  //加入集合
 
        /**
         * 模拟vertex顶点删除, 该顶点所有的边的to顶点的入度需要-1
         */
        vertex.outEdges.forEach((Edge<V,E> edge)->{
            int inSize = ins.get(edge.to) - 1;
            if(inSize == 0){
                queue.offer(edge.to);
            }else{
                ins.put(edge.to,inSize);
            }
        });
    }
 
    return list;
}