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.0KB

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