aboutsummaryrefslogtreecommitdiff
path: root/src/com.example.portfolio3/com/example/portfolio3/GraphAlgorithms.java
blob: 61ef6c24eff3d0872efb3716494651eaea56c8f6 (plain)
  1. package com.example.portfolio3;
  2. // origin: <https://moodle.ruc.dk/course/section.php?id=211877>
  3. import java.io.BufferedReader;
  4. import java.io.FileReader;
  5. import java.io.FileWriter;
  6. import java.io.IOException;
  7. import java.io.PrintWriter;
  8. import java.util.ArrayList;
  9. import java.util.Collection;
  10. import java.util.Collections;
  11. import java.util.HashMap;
  12. import java.util.HashSet;
  13. import java.util.List;
  14. import java.util.Set;
  15. /// foo
  16. public class GraphAlgorithms {
  17. /// foo
  18. GraphAlgorithms() { }
  19. /// calculate the length of a path or another collection of edges
  20. ///
  21. /// does not require the edges to form a path
  22. ///
  23. /// @param edges foo
  24. /// @return int
  25. public static int pathLength(final Collection<Edge> edges) {
  26. return edges.stream().mapToInt(e -> e.weight()).sum();
  27. }
  28. /// checks whether a list of edges form a path so that
  29. ///
  30. /// the to-vertex in one edge is the from-vertex of the next
  31. ///
  32. /// @param edges foo
  33. /// @return boolean
  34. public static boolean isPath(final List<Edge> edges) {
  35. for (int i = 1; i < edges.size(); i++) {
  36. if (edges.get(i - 1).to() != edges.get(i).from()
  37. ) {
  38. return false;
  39. }
  40. }
  41. return true;
  42. }
  43. /// Calculates the length of a path vertices in a graph
  44. ///
  45. /// return null if vertices are not connected as a path
  46. ///
  47. /// @param g foo
  48. /// @param path foo
  49. /// @return Integer
  50. public static Integer pathLength(
  51. final Graph g, final List<Vertex> path
  52. ) {
  53. int length = 0;
  54. for (int i = 1; i < path.size(); i++) {
  55. Integer w = g.getWeight(
  56. path.get(i - 1), path.get(i));
  57. if (w == null) {
  58. return null;
  59. }
  60. length += w;
  61. }
  62. return length;
  63. }
  64. //------------------------------------------------------------
  65. //
  66. // Comparators and sorting methods
  67. /// Comparator of edges based on weight
  68. ///
  69. /// can be used for sorting a list of edges
  70. ///
  71. /// @param e1 foo
  72. /// @param e2 foo
  73. /// @return int
  74. static int cmpEdgeWeight(final Edge e1, final Edge e2) {
  75. int w1 = e1.weight(), w2 = e2.weight();
  76. if (w1 != w2) {
  77. return w1 - w2;
  78. }
  79. if (e1.from() != e2.from()) {
  80. return e1.from().name().compareTo(
  81. e2.from().name());
  82. }
  83. return e1.to().name().compareTo(e2.to().name());
  84. }
  85. /// Comparator of edges based on from-vertex
  86. ///
  87. /// can be used for sorting a list of edges
  88. ///
  89. /// @param e1 foo
  90. /// @param e2 foo
  91. /// @return int
  92. static int cmpEdgeFrom(final Edge e1, final Edge e2) {
  93. if (e1.from() != e2.from()) {
  94. return e1.from().name().compareTo(
  95. e2.from().name());
  96. }
  97. int w1 = e1.weight(), w2 = e2.weight();
  98. if (w1 != w2) {
  99. return w1 - w2;
  100. }
  101. return e1.to().name().compareTo(e2.to().name());
  102. }
  103. /// Comparator of edges based on from-vertex
  104. ///
  105. /// can be used for sorting a list of edges
  106. ///
  107. /// @param e1 foo
  108. /// @param e2 foo
  109. /// @return int
  110. static int cmpEdgeTo(final Edge e1, final Edge e2) {
  111. if (e1.to() != e2.to()) {
  112. return e1.to().name().compareTo(
  113. e2.to().name());
  114. }
  115. if (e1.from() != e2.from()) {
  116. return e1.from().name().compareTo(
  117. e2.from().name());
  118. }
  119. int w1 = e1.weight(), w2 = e2.weight();
  120. return w1 - w2;
  121. }
  122. /// sort a collection of edges based on their weights
  123. ///
  124. /// @param edges foo
  125. /// @return List<Edge>
  126. static List<Edge> sortEdges(final Collection<Edge> edges) {
  127. ArrayList<Edge> list = new ArrayList<>(edges);
  128. Collections.sort(list, GraphAlgorithms::cmpEdgeWeight);
  129. return list;
  130. }
  131. /// sort a collection of edges based on from-vertex
  132. ///
  133. /// @param edges foo
  134. /// @return List<Edge>
  135. static List<Edge> sortEdgesFrom(final Collection<Edge> edges) {
  136. ArrayList<Edge> list = new ArrayList<>(edges);
  137. Collections.sort(list, GraphAlgorithms::cmpEdgeFrom);
  138. return list;
  139. }
  140. /// sort a collection of edges based on to-vertex
  141. ///
  142. /// @param edges foo
  143. /// @return List<Edge>
  144. static List<Edge> sortEdgesTo(final Collection<Edge> edges) {
  145. ArrayList<Edge> list = new ArrayList<>(edges);
  146. Collections.sort(list, GraphAlgorithms::cmpEdgeTo);
  147. return list;
  148. }
  149. /// sort a collection of vertices based on their name
  150. ///
  151. /// @param vertices foo
  152. /// @return List<Vertex>
  153. public static List<Vertex> sortVertex(
  154. final Collection<Vertex> vertices
  155. ) {
  156. ArrayList<Vertex> list = new ArrayList<>(vertices);
  157. Collections.sort(list, (Vertex v1, Vertex v2) ->
  158. v1.name().compareTo(v2.name()));
  159. return list;
  160. }
  161. //------------------------------------------------------------
  162. //
  163. // Algorithms for traverse and minimum spanning tree
  164. /// traverse a graph depth first from a given vertex
  165. /// return the set of visited vertices
  166. ///
  167. /// @param g foo
  168. /// @param v foo
  169. /// @return Set<Vertex>
  170. public static Set<Vertex> visitBreadthFirst(
  171. final Graph g, final Vertex v
  172. ) {
  173. HashSet<Vertex> thisLevel = new HashSet<>();
  174. HashSet<Vertex> nextLevel = new HashSet<>();
  175. HashSet<Vertex> visited = new HashSet<>();
  176. thisLevel.add(v);
  177. while (thisLevel.size() > 0) {
  178. System.out.println("level " + thisLevel);
  179. for (Vertex w:thisLevel) {
  180. //System.out.println("visited " + w);
  181. visited.add(w);
  182. Collection<Edge> outedge = g.outEdge(w);
  183. if (outedge == null) {
  184. continue;
  185. }
  186. for (Edge e: outedge) {
  187. if (visited.contains(e.to())) {
  188. continue;
  189. }
  190. if (thisLevel.contains(e.to())) {
  191. continue;
  192. }
  193. nextLevel.add(e.to());
  194. }
  195. }
  196. thisLevel = nextLevel;
  197. nextLevel = new HashSet<Vertex>();
  198. }
  199. return visited;
  200. }
  201. /// traverse a graph depth first from a given vertex
  202. /// return the set of visited vertices
  203. ///
  204. /// @param g foo
  205. /// @param v foo
  206. /// @return Set<Vertex>
  207. public static Set<Vertex> visitDepthFirst(
  208. final Graph g, final Vertex v
  209. ) {
  210. HashSet<Vertex> visit = new HashSet<>();
  211. visitDepthFirst(g, v, visit);
  212. return visit;
  213. }
  214. /// foo
  215. ///
  216. /// @param g foo
  217. /// @param v foo
  218. /// @param visited foo
  219. private static void visitDepthFirst(
  220. final Graph g, final Vertex v, final Set<Vertex> visited
  221. ) {
  222. if (visited.contains(v)) {
  223. return;
  224. }
  225. //System.out.println("visited "+v);
  226. visited.add(v);
  227. for (Edge e: g.outEdge(v)) {
  228. visitDepthFirst(g, e.to(), visited);
  229. }
  230. }
  231. /// an implementation of Prim's algorithm
  232. ///
  233. /// naive implementation without priorityqueue
  234. ///
  235. /// @param g foo
  236. /// @return Set<Edge>
  237. public static Set<Edge> minimumSpanningTree(final Graph g) {
  238. Collection<Edge> edges = g.edges();
  239. HashSet<Edge> mst = new HashSet<>();
  240. HashSet<Vertex> frontier = new HashSet<>();
  241. for (Edge e:edges) {
  242. frontier.add(e.from());
  243. break;
  244. }
  245. while (true) {
  246. Edge nearest = null;
  247. for (Edge e: edges) {
  248. if (!frontier.contains(e.from())) {
  249. continue;
  250. }
  251. if (frontier.contains(e.to())) {
  252. continue;
  253. }
  254. if (nearest == null
  255. || nearest.weight() > e.weight()
  256. ) {
  257. nearest = e;
  258. }
  259. }
  260. if (nearest == null) {
  261. break;
  262. }
  263. mst.add(nearest);
  264. frontier.add(nearest.to());
  265. }
  266. return mst;
  267. }
  268. /// returns the tree of shortest paths
  269. /// from start to all vertices in the graph
  270. ///
  271. /// naive implementation without a prorityqueue
  272. ///
  273. /// @param g foo
  274. /// @param start foo
  275. /// @return Set<Edge>
  276. public static Set<Edge> dijkstra(
  277. final Graph g, final Vertex start
  278. ) {
  279. // create table for done, prev and weight from start
  280. int maxint = Integer.MAX_VALUE;
  281. HashSet<Vertex> done = new HashSet<>();
  282. HashMap<Vertex, Edge> prev = new HashMap<>();
  283. HashMap<Vertex, Integer> weight = new HashMap<>();
  284. for (Vertex w: g.vertices()) {
  285. weight.put(w, maxint);
  286. }
  287. // start node is done, distance 0 from start
  288. weight.put(start, 0);
  289. done.add(start);
  290. while (true) {
  291. // find nearest from a done vertex
  292. Vertex nearest = null;
  293. int neardist = maxint;
  294. Edge done2near = null;
  295. for (Vertex w1:done) {
  296. for (Edge e :g.outEdge(w1)) {
  297. Vertex w2 = e.to();
  298. if (done.contains(w2)) {
  299. continue;
  300. }
  301. if ((weight.get(w1)
  302. + e.weight())
  303. < neardist
  304. ) {
  305. nearest = e.to();
  306. neardist = weight.get(w1)
  307. + e.weight();
  308. done2near = e;
  309. }
  310. }
  311. }
  312. //System.out.println("find nearest "+done2near);
  313. // if no more, then we are done
  314. if (nearest == null) {
  315. break;
  316. }
  317. // update distance from this node to other nodes
  318. for (Edge e1 : g.outEdge(nearest)) {
  319. Vertex w3 = e1.to();
  320. int wght = e1.weight();
  321. if (weight.get(w3) > (neardist + wght)) {
  322. weight.put(w3, neardist + wght);
  323. }
  324. }
  325. done.add(nearest);
  326. prev.put(nearest, done2near);
  327. weight.put(nearest, neardist);
  328. }
  329. return new HashSet<Edge>(prev.values());
  330. }
  331. //------------------------------------------------------------
  332. //
  333. // IO operations
  334. /// read a comma-separated file
  335. /// in the format <vertex> , <vertex> , <weight>
  336. ///
  337. /// stores file as bidirectional graph
  338. ///
  339. /// @param g foo
  340. /// @param file foo
  341. public static void readGraph(final Graph g, final String file) {
  342. try {
  343. BufferedReader in = new BufferedReader(
  344. new FileReader(file));
  345. for (String line = in.readLine();
  346. line != null;
  347. line = in.readLine()
  348. ) {
  349. if (line.length() == 0) {
  350. continue;
  351. }
  352. String[] arr = line.split(",");
  353. if (arr.length != 3) {
  354. throw new RuntimeException(
  355. "CSV file format error: "
  356. + line);
  357. }
  358. g.insertEdge(arr[0].trim(),
  359. arr[1].trim(),
  360. Integer.parseInt(arr[2].trim()));
  361. g.insertEdge(arr[1].trim(),
  362. arr[0].trim(),
  363. Integer.parseInt(arr[2].trim()));
  364. }
  365. in.close();
  366. } catch (IOException e) {
  367. throw new RuntimeException(e);
  368. }
  369. }
  370. /// foo
  371. ///
  372. /// @param g foo
  373. public static void printGraph(final Graph g) {
  374. for (Vertex v: sortVertex(g.vertices())) {
  375. System.out.println(v.toString());
  376. for (Edge e: sortEdgesTo(g.outEdge(v))) {
  377. System.out.println(" " + e.toString());
  378. }
  379. }
  380. }
  381. /// store a list of lines as a file
  382. ///
  383. /// @param list foo
  384. /// @param f foo
  385. public static void storeStrings(
  386. final List<String> list, final String f
  387. ) {
  388. try {
  389. PrintWriter out = new PrintWriter(
  390. new FileWriter(f));
  391. for (String s: list) {
  392. out.println(s);
  393. }
  394. out.close();
  395. } catch (IOException e) {
  396. throw new RuntimeException(e);
  397. }
  398. }
  399. /// read a file a returns a list of lines
  400. ///
  401. /// @param f foo
  402. /// @return ArrayList
  403. public static ArrayList<String> loadStrings(final String f) {
  404. ArrayList<String> list = new ArrayList<>();
  405. try {
  406. BufferedReader in = new BufferedReader(
  407. new FileReader(f));
  408. while (true) {
  409. String s = in.readLine();
  410. if (s == null) {
  411. break;
  412. }
  413. list.add(s);
  414. }
  415. in.close();
  416. } catch (IOException e) {
  417. throw new RuntimeException(e);
  418. }
  419. return list;
  420. }
  421. }