赞
踩
目录
五、AOV网(Activity On Vertex Network)
- package graph;
-
- public interface Graph<V,E> {
- // 边的数量
- int edgesSize();
- // 顶点数量
- int verticesSize();
-
- // 添加顶点
- void addVertex(V v);
- // 添加边(无权值)
- void addEdge(V from,V to);
- // 添加边(有权值)
- void addEdge(V from,V to,E weight);
-
- // 删除顶点
- void removeVertex(V v);
- // 删除边
- void removeEdge(V from,V to);
-
- interface vertexVisitor<V>{
- boolean visit(V v);
- }
-
- void print();
- }

- /**
- * 添加无权值的边
- * @param from
- * @param to
- */
- @Override
- public void addEdge(V from, V to) {
- addEdge(from,to,null);
- }
-
- /**
- * 添加有权值的边
- * @param from
- * @param to
- * @param weight
- */
- @Override
- public void addEdge(V from, V to, E weight) {
- //根据传入的参数from找到出发点,如果不存在则创建
- Vertex<V,E> fromVertex=vertices.get(from);
- if (fromVertex==null){
- fromVertex=new Vertex<>(from);
- //将点和对应的点关系存入
- vertices.put(from,fromVertex);
- }
- //根据传入参数to找到终点,如果找不到则创建
- Vertex<V,E> toVertex=vertices.get(to);
- if (toVertex==null){
- toVertex=new Vertex<>(to);
- //将点和对应的点关系存入
- vertices.put(to,toVertex);
- }
- //根据出发点和终点,创建边
- Edge<V,E> edge=new Edge<>(fromVertex,toVertex);
- edge.weight=weight;//有权值加上,无权值则为null
-
- //不管原来是否存在,都先删除,在添加进去
- if (fromVertex.outEdges.contains(edge)){ //说明存在
- toVertex.inEdges.remove(edge);
- //在整个图中的边减少
- edges.remove(edge);
- }
- fromVertex.outEdges.add(edge);
- toVertex.inEdges.add(edge);
- //在整个图中的边增加
- edges.add(edge);
-
- }

- public int edgesSize() {
- return edges.size();
- }
- /**
- * 顶点
- * @param <V>
- * @param <E>
- */
- private static class Vertex<V,E>{
- //顶点值
- V value;
- Set<Edge<V,E>> inEdges=new HashSet<>();
- Set<Edge<V,E>> outEdges=new HashSet<>();
-
- public Vertex(V value) {
- this.value = value;
- }
-
- /**
- * 比较两个顶点是否相等
- * @param obj
- * @return
- */
- @Override
- public boolean equals(Object obj) {
- return Objects.equals(value,((Vertex<V,E>)obj).value);
- }
-
- @Override
- public int hashCode() {
- return value==null ? 0: value.hashCode();
- }
- }

- /**
- * 边
- * @param <V>
- * @param <E>
- */
- private static class Edge<V,E> {
- Vertex<V,E> from;
- Vertex<V,E> to;
- E weight;
-
- public Edge(Vertex<V, E> from, Vertex<V, E> to) {
- this.from = from;
- this.to = to;
- }
-
- /**
- * 判断两条边是否相同
- * @param obj
- * @return
- */
- @Override
- public boolean equals(Object obj) {
- Edge<V,E> edge=(Edge<V, E>) obj;
- return Objects.equals(from,edge.from) && Objects.equals(to,edge.to);
- }
-
- /**
- * 如果equals的结果是true说明一样,则hashcode值也一样
- * @return
- */
- @Override
- public int hashCode() {
- return from.hashCode()+to.hashCode();
- }
- }

- /**
- * 删除边
- * @param from
- * @param to
- */
- @Override
- public void removeEdge(V from, V to) {
- // 根据传入的from获得起点,不存在则不需要删除
- Vertex<V,E> fromVertex=vertices.get(from);
- if (fromVertex ==null){
- return;
- }
- // 根据传入的to找到终点,不存在则不需要删除
- Vertex<V,E> toVertex=vertices.get(to);
- if (toVertex==null) return;
-
- // 根据起点和终点获得边,然后删除
- Edge<V,E> edge=new Edge<>(fromVertex,toVertex);
- if (fromVertex.outEdges.remove(edge)){ //表示删除成功
- toVertex.inEdges.remove(edge);
- edges.remove(edge);
- }
- }

- package graph;
-
- import java.util.*;
-
- public class ListGraph<V,E> implements Graph<V,E>{
-
- // 传入的V与顶点类Vertex的映射【要记录该点的出入是inEdges还是outEdges】
- private Map<V, Vertex<V, E>> vertices = new HashMap<>();
- // 边的Set集合
- private Set<Edge<V, E>> edges = new HashSet<>();
-
- public void print(){
- System.out.println("[顶点]-------------------");
- vertices.forEach((V v, Vertex<V, E> vertex) -> {
- System.out.println(v);
- System.out.println("out-----------");
- System.out.println(vertex.outEdges);
- System.out.println("int-----------");
- System.out.println(vertex.inEdges);
- });
- System.out.println("[边]-------------------");
- edges.forEach((Edge<V, E> edge) -> {
- System.out.println(edge);
- });
- }
-
- @Override
- public int edgesSize() {
- return edges.size();
- }
-
- @Override
- public int verticesSize() {
- return vertices.size();
- }
-
- @Override
- public void addVertex(V v) {
- // put(key,value)
- vertices.put(v,new Vertex<>(v));
- }
-
- /**
- * 添加无权值的边
- * @param from
- * @param to
- */
- @Override
- public void addEdge(V from, V to) {
- addEdge(from,to,null);
- }
-
- /**
- * 添加有权值的边
- * @param from
- * @param to
- * @param weight
- */
- @Override
- public void addEdge(V from, V to, E weight) {
- //根据传入的参数from找到出发点,如果不存在则创建
- Vertex<V,E> fromVertex=vertices.get(from);
- if (fromVertex==null){
- fromVertex=new Vertex<>(from);
- //将点和对应的点关系存入
- vertices.put(from,fromVertex);
- }
- //根据传入参数to找到终点,如果找不到则创建
- Vertex<V,E> toVertex=vertices.get(to);
- if (toVertex==null){
- toVertex=new Vertex<>(to);
- //将点和对应的点关系存入
- vertices.put(to,toVertex);
- }
- //根据出发点和终点,创建边
- Edge<V,E> edge=new Edge<>(fromVertex,toVertex);
- edge.weight=weight;//有权值加上,无权值则为null
-
- //不管原来是否存在,都先删除,在添加进去
- if (fromVertex.outEdges.contains(edge)){ //说明存在
- toVertex.inEdges.remove(edge);
- //在整个图中的边减少
- edges.remove(edge);
- }
- fromVertex.outEdges.add(edge);
- toVertex.inEdges.add(edge);
- //在整个图中的边增加
- edges.add(edge);
-
- }
-
- /**
- * 删除点
- * @param v
- */
- @Override
- public void removeVertex(V v) {
- //根据传入的值找到点并且删除,不存在则不做操作
- Vertex<V,E> vertex=vertices.remove(v);
- if (vertex==null) return;
- // 迭代器遍历集合vertex.outEdges,删除所有从该点出去的边
- // iterator.hasNext():相当于i++
- for (Iterator<Edge<V,E>> iterator = vertex.inEdges.iterator();iterator.hasNext();){
- Edge<V,E> edge=iterator.next();//遍历从该点出去的边
- edge.to.inEdges.remove(edge);//获取终点进入的边,并从中删除遍历到的边
- //这个remove方法是将当前的这个边删除
- iterator.remove();//将当前遍历到当前遍历到的元素edge从集合vertex.outEdges中删除
- edges.remove(edge);
- }
- // 迭代器遍历集合vertex.inEdges, 删除所有进入该点的边
- for (Iterator<Edge<V, E>> iterator = vertex.inEdges.iterator(); iterator.hasNext();) {
- Edge<V, E> edge = iterator.next(); // 遍历到的进入该点的边
- edge.from.outEdges.remove(edge); // 获取起点出去的边,并从中删除遍历到的边
- iterator.remove(); // 将当前遍历到的元素edge从集合vertex.inEdges中删掉
- edges.remove(edge);
- }
- }
-
- /**
- * 删除边
- * @param from
- * @param to
- */
- @Override
- public void removeEdge(V from, V to) {
- // 根据传入的from获得起点,不存在则不需要删除
- Vertex<V,E> fromVertex=vertices.get(from);
- if (fromVertex ==null){
- return;
- }
- // 根据传入的to找到终点,不存在则不需要删除
- Vertex<V,E> toVertex=vertices.get(to);
- if (toVertex==null) return;
-
- // 根据起点和终点获得边,然后删除
- Edge<V,E> edge=new Edge<>(fromVertex,toVertex);
- if (fromVertex.outEdges.remove(edge)){ //表示删除成功
- toVertex.inEdges.remove(edge);
- edges.remove(edge);
- }
- }
-
- /**
- * 顶点
- * @param <V>
- * @param <E>
- */
- private static class Vertex<V,E>{
- //顶点值
- V value;
- Set<Edge<V,E>> inEdges=new HashSet<>();
- Set<Edge<V,E>> outEdges=new HashSet<>();
-
- public Vertex(V value) {
- this.value = value;
- }
-
- /**
- * 比较两个顶点是否相等
- * @param obj
- * @return
- */
- @Override
- public boolean equals(Object obj) {
- return Objects.equals(value,((Vertex<V,E>)obj).value);
- }
-
- @Override
- public int hashCode() {
- return value==null ? 0: value.hashCode();
- }
- }
-
- /**
- * 边
- * @param <V>
- * @param <E>
- */
- private static class Edge<V,E> {
- Vertex<V,E> from;
- Vertex<V,E> to;
- E weight;
-
- public Edge(Vertex<V, E> from, Vertex<V, E> to) {
- this.from = from;
- this.to = to;
- }
-
- /**
- * 判断两条边是否相同
- * @param obj
- * @return
- */
- @Override
- public boolean equals(Object obj) {
- Edge<V,E> edge=(Edge<V, E>) obj;
- return Objects.equals(from,edge.from) && Objects.equals(to,edge.to);
- }
-
- /**
- * 如果equals的结果是true说明一样,则hashcode值也一样
- * @return
- */
- @Override
- public int hashCode() {
- return from.hashCode()+to.hashCode();
- }
- }
- }

图的遍历
从图中某一顶点出发访问图中其余顶点,且每一个顶点仅被访问一次
图有2种常见的遍历方式(有向图、无向图都适用)
发明“深度优先搜索”算法的2位科学家在1986年共同获得计算机领域的最高奖:图灵奖。
- package graph;
-
- import java.util.List;
- import java.util.Set;
-
- public interface Graph<V,E> {
- // 边的数量
- int edgesSize();
- // 顶点数量
- int verticesSize();
-
- // 添加顶点
- void addVertex(V v);
- // 添加边(无权值)
- void addEdge(V from,V to);
- // 添加边(有权值)
- void addEdge(V from,V to,E weight);
-
- // 删除顶点
- void removeVertex(V v);
- // 删除边
- void removeEdge(V from,V to);
-
- void print();
-
-
-
- void bfs(V begin, vertexVisitor<V> visitor); // 广度优先搜索
-
-
- /**
- * 非递归版的深度优先搜索
- * @param begin
- * @param visitor
- */
- void dfs(V begin, vertexVisitor<V> visitor);
-
-
- List<V> topologicalSort(); // 拓扑排序
-
- interface vertexVisitor<V>{
- boolean visit(V v);
- }
- }

之前所学的二叉树层序遍历就是一种广度优先搜索。
注:BFS结果不唯一。
经历一层可以访问到的
将该点从队列取出来,然后再将其所子节点依次放入队列
从某个点开始,将它可以到达的点放入队列,如果已经访问过则跳过,然后从队列中取出点重复该过程。
第一层:假设从点A开始,它可以到达B、F,则将B、F入队。
此时队列中元素 [B、F]
第二层:队头B出队,B可以到达C、I、G,将C、I、G入队。
此时队列中元素 [F、C、I、G]
第三层:队头F出队,F可以到达G、E,但G已访问过,将E入队。
此时队列中元素 [C、I、G、E]
第四层:队头C出队,C可以到达I、D,但I已访问过,将D入队。
此时队列中元素 [I、G、E、D]
第五层:队头I出队,I可以到达D,但D已访问过,不执行操作。
此时队列中元素 [G、E、D]
第六层:队头G出队,G可以到达D、H,但D已访问过,将H入队。
此时队列中元素 [E、D、H]
第七层:队头E出队,E可以到达D、H、F,都访问过,不执行操作。
此时队列中元素 [D、H]
第八层:队头D出队,D可以到达C、H、E,都访问过,不执行操作。
此时队列中元素 [H]
第九层:队头H出队,H可以到达D、G、E,都访问过,不执行操作。
此时队列中元素 []
队列为空,广度优先搜索结束。
- /**
- * 广度优先搜索:BFS
- * @param begin
- * @param visitor
- */
- @Override
- public void bfs(V begin, vertexVisitor<V> visitor) {
- if (visitor ==null) return;
- //根据传入的值begin找到顶点
- Vertex<V,E> beginVertex=vertices.get(begin);
- //该顶点不存在,不做操作
- if (beginVertex==null) return;
-
- //存放已经访问过的节点
- //因为如果不存放,则遍历到下一个顶点的时候,会将刚刚出队列的数值再一次放入
- Set<Vertex<V,E>> visitedVertices=new HashSet<>();
- //临时存放
- Queue<Vertex<V,E>> queue=new LinkedList<>();
- queue.offer(beginVertex); //元素入队
- //将已经加入过队列的元素标记一下
- visitedVertices.add(beginVertex);
-
- //思考参考二叉树层次遍历,队列存放每一层的顶点,用集合记录已经访问过的点
- while (!queue.isEmpty()){
- //从队列中取出第一个顶点
- Vertex<V,E> vertex=queue.poll();
- if (visitor.visit(vertex.value)) return;
- //遍历【从队列中取出的顶点】的出去的边,将【这些边的终点】入队,并且标记为已经访问过
- for (Edge<V,E> edge:vertex.outEdges){
- //如果集合中已经记录该顶点,说明已经访问过,跳过进行下一轮
- if (visitedVertices.contains(edge.to)) continue;
- queue.offer(edge.to);
- visitedVertices.add(edge.to);
- }
- }
- }

之前所学的二叉树前序遍历就是一种深度优先搜索。
注:DFS结果不唯一。
- /**
- * 递归实现--->深度优先遍历:DFS
- * @param begin
- * @param visitedVertices 已经访问过的节点
- */
- /**
- * 递归实现深度优先搜索DFS
- */
- public void dfs(V begin) {
- Vertex<V, E> beginVertex = vertices.get(begin); // 根据传入的值获取顶点
- if (beginVertex == null) return; // 顶点不存在则不执行操作
- dfs(beginVertex, new HashSet<>()); // 传入的集合,用来记录访问过的顶点
- }
- private void dfs(Vertex<V, E> vertex, Set<Vertex<V, E>> vistedVertices){
- System.out.println(vertex.value);
- vistedVertices.add(vertex);
-
- for(Edge<V, E> edge : vertex.outEdges){
- if(vistedVertices.contains(edge.to)) continue;
- //dfs会自己后退
- dfs(edge.to, vistedVertices);
- }
- }

1.从outEdge中选择一条边
2.将选择边的from和to边,按顺序入栈【先入from再入to】
3.打印选择边的to
4.将to边添加到已经访问的范围中
5.break
- /**
- * 非递归版(通过栈实现):深度优先搜索(DFS)
- * @param
- */
-
- @Override
- public void dfs(V begin, vertexVisitor<V> visitor) {
- if (visitor ==null) return;
-
- //查看该点是否存在
- Vertex<V,E> beginVertex=vertices.get(begin);
- if (begin==null) return;
- //创建一个set存放已经访问过的节点
- Set<Vertex<V,E>> visitedVertices=new HashSet<>();
- //创建一个栈临时存放
- Stack<Vertex<V,E>> stack=new Stack<>();
-
- //先访问起点
- stack.push(beginVertex);
- visitedVertices.add(beginVertex);
- //如果该节点已经访问过,则不再放入
- if (visitor.visit(begin)) return;
-
- while (!stack.isEmpty()){ //如果栈不为空
- Vertex<V,E> vertex=stack.pop();
-
- for (Edge<V,E> edge :vertex.outEdges){ //访问出发点的每一条边
- if (visitedVertices.contains(edge.to)) continue; //表示这条边已经访问过
- //找了一条适合且未遍历过的边
- stack.push(edge.from);//将出发点加入栈中
- stack.push(edge.to);//将出度点加入栈中
- //将加入栈中的节点加入已经访问过发范围中
- visitedVertices.add(edge.to);
- if (visitor.visit(edge.to.value)) return;
- break;
- }
- }
- }

一项大的工程常被分为多个小的子工
子工程之间可能存在一定的先后顺序,即某些子工程必须在其他的一些子工程完成后才能开始。
在现代化管理中,人们常用有向图来描述和分析一项工程的计划和实施过程,子工程被称为活动(Activity)
以顶点表示活动、有向边表示活动之间的先后关系,这样的图简称为 AOV 网。【有很强的依赖关系】
标准的AOV网必须是一个有向无环图(Directed Acyclic Graph,简称 DAG)
前驱活动:有向边起点的活动称为终点的前驱活动
后继活动:有向边终点的活动称为起点的后继活动
可以使用卡恩算法(Kahn于1962年提出)完成拓扑排序。
假设 L 是存放拓扑排序结果的列表:
- ① 把所有入度为 0 的顶点放入 queue 中,然后把这些顶点从图中去掉,放入list中
- ② 然后找到已经从queue中移除的入度为0的节点的下一个节点,将下一个节点的入度-1,如果-1后,入度为0,则直接加入queue
- ③ 重复操作 ①,直到找不到入度为 0 的顶点
- 如果此时 L 中的元素个数和顶点总数相同,说明拓扑排序完成
- 如果此时 L 中的元素个数少于顶点总数,说明原图中存在环,无法进行拓扑排序
- /**
- * 拓扑排序
- * @return
- */
- @Override
- public List<V> topologicalSort() {
- List<V> list = new ArrayList<>();
- Queue<Vertex<V, E>> queue = new LinkedList<>();
- Map<Vertex<V, E>, Integer> ins = new HashMap<>();
-
- // 初始化(将度为0的节点放入队列)
- vertices.forEach((V v, Vertex<V, E> vertex) -> {
- int indegree = vertex.inEdges.size(); // 入度
- if(indegree == 0) { // 入度为0,放入队列
- queue.offer(vertex);
- } else { // 入度不为0,用map记录它的入度
- ins.put(vertex, indegree);
- }
- });
-
- while(!queue.isEmpty()){ // 从队列中取节点
- //取出队列中的第一个元素
- Vertex<V, E> vertex = queue.poll();
- list.add(vertex.value); // 放入返回结果中
-
- //遍历取出的节点的出度边
- for (Edge<V, E> edge : vertex.outEdges){
- // 队列中取出节点所通向节点的入度
- int toIndegree = ins.get(edge.to) - 1;
- if(toIndegree == 0) { // 入度为0,放入队列
- queue.offer(edge.to);
- } else { // 入度不为0,用map记录它的入度
- ins.put(edge.to, toIndegree);
- }
- }
- }
-
- return list;
- }

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。