携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第27天,点击查看活动详情
AOV网
在现代化管理中,人们常用有向图来描述和分析一项工程的计划和实施过程,一个工程常被分为多个小的子工程,这些子工程被称为活动(Activity),在有向图中若以顶点表示活动,有向边表示活动之间的先后关系,这样的图简称为AOV网。
标准的AOV网必须是一个有向无环图(Directed Acyclic Graph,简称 DAG)
拓扑排序
前驱活动: 有向边起点的活动称为终点的前驱活动 (注意: 只有当一个活动的前驱全部都完成后,这个活动才能进行)
后继活动: 有向边终点的活动称为起点的后继活动
什么是拓扑排序
将AOV网中所有活动排成一个序列,使得每个活动的前驱活动都排在该活动的前面
比如上图的拓扑排序结果是:A、B、C、D、E、F 或者 A、B、D、C、E、F (结果并不一定是唯一的)
拓扑排序
可以使用卡恩算法(Kahn)完成拓扑排序
假设 L 是存放拓扑排序结果的列表
(1) 把所有入度为 0 的顶点放入 L 中,然后把这些顶点从图中去掉
(2) 重复操作(1),直到找不到入度为 0 的顶点
- 如果此时 L 中的元素个数和顶点总数相同,说明拓扑排序完成
- 如果此时 L 中的元素个数少于顶点总数,说明原图中存在环,无法进行拓扑排序
代码实现
/**
* 拓扑排序(卡恩算法)
*/
@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;
}