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