You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DirectedGraph.java 5.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. /*
  2. * Copyright (C) 2012-present the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.pf4j.util;
  17. import java.util.ArrayList;
  18. import java.util.Collections;
  19. import java.util.HashMap;
  20. import java.util.List;
  21. import java.util.Map;
  22. import java.util.Stack;
  23. /**
  24. * See <a href="https://en.wikipedia.org/wiki/Directed_graph">Wikipedia</a> for more information.
  25. *
  26. * @author Decebal Suiu
  27. */
  28. public class DirectedGraph<V> {
  29. /**
  30. * The implementation here is basically an adjacency list, but instead
  31. * of an array of lists, a Map is used to map each vertex to its list of
  32. * adjacent vertices.
  33. */
  34. private Map<V, List<V>> neighbors = new HashMap<>();
  35. /**
  36. * Add a vertex to the graph. Nothing happens if vertex is already in graph.
  37. */
  38. public void addVertex(V vertex) {
  39. if (containsVertex(vertex)) {
  40. return;
  41. }
  42. neighbors.put(vertex, new ArrayList<V>());
  43. }
  44. /**
  45. * True if graph contains vertex.
  46. */
  47. public boolean containsVertex(V vertex) {
  48. return neighbors.containsKey(vertex);
  49. }
  50. public void removeVertex(V vertex) {
  51. neighbors.remove(vertex);
  52. }
  53. /**
  54. * Add an edge to the graph; if either vertex does not exist, it's added.
  55. * This implementation allows the creation of multi-edges and self-loops.
  56. */
  57. public void addEdge(V from, V to) {
  58. addVertex(from);
  59. addVertex(to);
  60. neighbors.get(from).add(to);
  61. }
  62. /**
  63. * Remove an edge from the graph. Nothing happens if no such edge.
  64. * @throws {@link IllegalArgumentException} if either vertex doesn't exist.
  65. */
  66. public void removeEdge(V from, V to) {
  67. if (!containsVertex(from)) {
  68. throw new IllegalArgumentException("Nonexistent vertex " + from);
  69. }
  70. if (!containsVertex(to)) {
  71. throw new IllegalArgumentException("Nonexistent vertex " + to);
  72. }
  73. neighbors.get(from).remove(to);
  74. }
  75. public List<V> getNeighbors(V vertex) {
  76. return containsVertex(vertex) ? neighbors.get(vertex) : new ArrayList<V>();
  77. }
  78. /**
  79. * Report (as a Map) the out-degree (the number of tail ends adjacent to a vertex) of each vertex.
  80. */
  81. public Map<V, Integer> outDegree() {
  82. Map<V, Integer> result = new HashMap<>();
  83. for (V vertex : neighbors.keySet()) {
  84. result.put(vertex, neighbors.get(vertex).size());
  85. }
  86. return result;
  87. }
  88. /**
  89. * Report (as a Map) the in-degree (the number of head ends adjacent to a vertex) of each vertex.
  90. */
  91. public Map<V, Integer> inDegree() {
  92. Map<V, Integer> result = new HashMap<>();
  93. for (V vertex : neighbors.keySet()) {
  94. result.put(vertex, 0); // all in-degrees are 0
  95. }
  96. for (V from : neighbors.keySet()) {
  97. for (V to : neighbors.get(from)) {
  98. result.put(to, result.get(to) + 1); // increment in-degree
  99. }
  100. }
  101. return result;
  102. }
  103. /**
  104. * Report (as a List) the topological sort of the vertices; null for no such sort.
  105. * See <a href="https://en.wikipedia.org/wiki/Topological_sorting">this</a> for more information.
  106. */
  107. public List<V> topologicalSort() {
  108. Map<V, Integer> degree = inDegree();
  109. // determine all vertices with zero in-degree
  110. Stack<V> zeroVertices = new Stack<>(); // stack as good as any here
  111. for (V v : degree.keySet()) {
  112. if (degree.get(v) == 0) {
  113. zeroVertices.push(v);
  114. }
  115. }
  116. // determine the topological order
  117. List<V> result = new ArrayList<>();
  118. while (!zeroVertices.isEmpty()) {
  119. V vertex = zeroVertices.pop(); // choose a vertex with zero in-degree
  120. result.add(vertex); // vertex 'v' is next in topological order
  121. // "remove" vertex 'v' by updating its neighbors
  122. for (V neighbor : neighbors.get(vertex)) {
  123. degree.put(neighbor, degree.get(neighbor) - 1);
  124. // remember any vertices that now have zero in-degree
  125. if (degree.get(neighbor) == 0) {
  126. zeroVertices.push(neighbor);
  127. }
  128. }
  129. }
  130. // check that we have used the entire graph (if not, there was a cycle)
  131. if (result.size() != neighbors.size()) {
  132. return null;
  133. }
  134. return result;
  135. }
  136. /**
  137. * Report (as a List) the reverse topological sort of the vertices; null for no such sort.
  138. */
  139. public List<V> reverseTopologicalSort() {
  140. List<V> list = topologicalSort();
  141. if (list == null) {
  142. return null;
  143. }
  144. Collections.reverse(list);
  145. return list;
  146. }
  147. /**
  148. * True if graph is a dag (directed acyclic graph).
  149. */
  150. public boolean isDag () {
  151. return topologicalSort() != null;
  152. }
  153. /**
  154. * String representation of graph.
  155. */
  156. @Override
  157. public String toString() {
  158. StringBuffer sb = new StringBuffer();
  159. for (V vertex : neighbors.keySet()) {
  160. sb.append("\n " + vertex + " -> " + neighbors.get(vertex));
  161. }
  162. return sb.toString();
  163. }
  164. }