package com.example.portfolio3; // origin: <https://moodle.ruc.dk/course/section.php?id=211877> import java.io.*; import java.util.*; /// foo public class GraphAlgorithms { /// foo GraphAlgorithms() {} /// Calculates the length of a path or any other collection of edes /// /// does not require the edges to form a path /// @param edges foo /// @return int public static int pathLength(Collection<Edge> edges){ return edges.stream().mapToInt(e-> e.weight()).sum(); } /// checks whether a list of edges form a path so that /// /// the to-vertex in one edge is the from-vertex of the next /// @param edges foo /// @return boolean public static boolean isPath(List<Edge> edges){ for(int i=1;i<edges.size();i++){ if(edges.get(i-1).to()!=edges.get(i).from())return false; } return true; } ///Calculates the length of a path vertices in a graph /// /// return null if vertices are not connected as a path /// @param g foo /// @param path foo /// @return Integer public static Integer pathLength(Graph g,List<Vertex> path){ int length=0; for(int i=1;i<path.size();i++){ Integer w=g.getWeight(path.get(i-1),path.get(i)); if(w==null)return null; length+=w; } return length; } //------------------------------------------------------------ // // Comparators and sorting methods /// Comparator of edges based on weight /// /// can be used for sorting a list of edges /// @param e1 foo /// @param e2 foo /// @return int static int cmpEdgeWeight(Edge e1,Edge e2) { int w1=e1.weight(),w2=e2.weight(); if(w1!=w2)return w1-w2; if(e1.from()!=e2.from())return e1.from().name().compareTo(e2.from().name()); return e1.to().name().compareTo(e2.to().name()); } /// Comparator of edges based on from-vertex /// /// can be used for sorting a list of edges /// @param e1 foo /// @param e2 foo /// @return int static int cmpEdgeFrom(Edge e1,Edge e2) { if(e1.from()!=e2.from())return e1.from().name().compareTo(e2.from().name()); int w1=e1.weight(),w2=e2.weight(); if(w1!=w2)return w1-w2; return e1.to().name().compareTo(e2.to().name()); } /// Comparator of edges based on from-vertex /// /// can be used for sorting a list of edges /// @param e1 foo /// @param e2 foo /// @return int static int cmpEdgeTo(Edge e1,Edge e2) { if(e1.to()!=e2.to())return e1.to().name().compareTo(e2.to().name()); if(e1.from()!=e2.from())return e1.from().name().compareTo(e2.from().name()); int w1=e1.weight(),w2=e2.weight(); return w1-w2; } /// sort a collection of edges based on their weights /// @param edges foo /// @return List<Edge> static List<Edge> sortEdges(Collection<Edge> edges){ ArrayList<Edge> list=new ArrayList<>(edges); Collections.sort(list,GraphAlgorithms::cmpEdgeWeight); return list; } /// sort a collection of edges based on from-vertex /// @param edges foo /// @return List<Edge> static List<Edge> sortEdgesFrom(Collection<Edge> edges){ ArrayList<Edge> list=new ArrayList<>(edges); Collections.sort(list,GraphAlgorithms::cmpEdgeFrom); return list; } /// sort a collection of edges based on to-vertex /// @param edges foo /// @return List<Edge> static List<Edge> sortEdgesTo(Collection<Edge> edges){ ArrayList<Edge> list=new ArrayList<>(edges); Collections.sort(list,GraphAlgorithms::cmpEdgeTo); return list; } /// sort a collection of vertices based on their name /// @param vertices foo /// @return List<Vertex> static List<Vertex> sortVertex(Collection<Vertex> vertices){ ArrayList<Vertex> list=new ArrayList<>(vertices); Collections.sort(list,(Vertex v1,Vertex v2)-> v1.name().compareTo(v2.name())); return list; } //------------------------------------------------------------ // // Algorithms for traverse and minimum spanning tree /// traverse a graph depth first from a given vertex /// return the set of visited vertices /// @param g foo /// @param v foo /// @return Set<Vertex> public static Set<Vertex> visitBreadthFirst(Graph g,Vertex v){ HashSet<Vertex> thisLevel=new HashSet<>(); HashSet<Vertex> nextLevel=new HashSet<>(); HashSet<Vertex> visited=new HashSet<>(); thisLevel.add(v); while(thisLevel.size()>0){ System.out.println("level "+thisLevel); for(Vertex w:thisLevel){ //System.out.println("visited "+w); visited.add(w); Collection<Edge> outedge=g.outEdge(w); if(outedge==null)continue; for(Edge e: outedge){ if(visited.contains(e.to()))continue; if(thisLevel.contains(e.to()))continue; nextLevel.add(e.to()); } } thisLevel=nextLevel; nextLevel=new HashSet<Vertex>(); } return visited; } /// traverse a graph depth first from a given vertex /// return the set of visited vertices /// @param g foo /// @param v foo /// @return Set<Vertex> public static Set<Vertex> visitDepthFirst(Graph g,Vertex v){ HashSet<Vertex> visit=new HashSet<>(); visitDepthFirst(g, v,visit); return visit; } /// foo /// @param g foo /// @param v foo /// @param visited foo private static void visitDepthFirst(Graph g,Vertex v,Set<Vertex> visited){ if(visited.contains(v))return; //System.out.println("visited "+v); visited.add(v); for(Edge e: g.outEdge(v)) visitDepthFirst(g,e.to(),visited); } /// an implementation of Prim's algorithm /// naive implementation without priorityqueue /// @param g foo /// @return Set<Edge> public static Set<Edge> minimumSpanningTree(Graph g){ Collection<Edge> edges=g.edges(); HashSet<Edge> mst=new HashSet<>(); HashSet<Vertex> frontier=new HashSet<>(); for(Edge e:edges){frontier.add(e.from());break;} while(true) { Edge nearest = null; for (Edge e : edges) { if (!frontier.contains(e.from())) continue; if (frontier.contains(e.to())) continue; if (nearest == null || nearest.weight() > e.weight()) nearest = e; } if(nearest==null)break; mst.add(nearest); frontier.add(nearest.to()); } return mst; } /// returns the tree of shortest paths from start to /// all vertices in the graph /// /// naive implementation without a prorityqueue /// @param g foo /// @param start foo /// @return Set<Edge> public static Set<Edge> dijkstra(Graph g, Vertex start){ // create table for done, prev and weight from start int maxint =Integer.MAX_VALUE; HashSet<Vertex> done=new HashSet<>(); HashMap<Vertex,Edge> prev=new HashMap<>(); HashMap<Vertex,Integer> weight=new HashMap<>(); for(Vertex w:g.vertices())weight.put(w,maxint); // start node is done, distance 0 from start weight.put(start,0); done.add(start); while(true){ // find nearest from a done vertex Vertex nearest = null; int neardist = maxint; Edge done2near=null; for(Vertex w1:done){ for (Edge e : g.outEdge(w1)) { Vertex w2 = e.to(); if (done.contains(w2)) continue; if ((weight.get(w1) + e.weight()) < neardist) { nearest = e.to(); neardist = weight.get(w1) + e.weight(); done2near = e; } } } // System.out.println("find nearest "+done2near); // if no more, then we are done if (nearest == null) break; // update distance from this node to other nodes for (Edge e1 : g.outEdge(nearest)) { Vertex w3 = e1.to(); int wght = e1.weight(); if (weight.get(w3) > (neardist + wght)) { weight.put(w3, neardist + wght); } } done.add(nearest); prev.put(nearest,done2near); weight.put(nearest,neardist); } return new HashSet<Edge>(prev.values()); } //------------------------------------------------------------ // // IO operations /// read a comma-separated file in the format /// <vertex> , <vertex> , <weight> /// /// stores file as bidirectional graph /// @param g foo /// @param file foo public static void readGraph(Graph g, String file) { try{ BufferedReader in = new BufferedReader(new FileReader(file)); for(String line=in.readLine(); line!=null; line=in.readLine()) { if(line.length()==0) continue; String[] arr = line.split(","); if(arr.length!=3) throw new RuntimeException("CSV file format error: "+line); g.insertEdge(arr[0].trim(), arr[1].trim(), Integer.parseInt(arr[2].trim())); g.insertEdge(arr[1].trim(), arr[0].trim(), Integer.parseInt(arr[2].trim())); } in.close(); }catch(IOException e){ throw new RuntimeException(e); } } /// foo /// @param g foo static void printGraph(Graph g) { for(Vertex v: sortVertex(g.vertices())) { System.out.println(v.toString()); for(Edge e:sortEdgesTo(g.outEdge(v))) System.out.println(" "+e.toString()); } } /// store a list of lines as a file /// @param list foo /// @param f foo public static void storeStrings(List<String> list,String f){ try{ PrintWriter out=new PrintWriter(new FileWriter(f)); for(String s:list){ out.println(s); } out.close(); }catch(IOException e){ throw new RuntimeException(e); } } /// read a file a returns a list of lines /// @param f foo /// @return ArrayList public static ArrayList<String> loadStrings(String f){ ArrayList<String> list=new ArrayList<>(); try{ BufferedReader in=new BufferedReader(new FileReader(f)); while(true){ String s=in.readLine(); if(s==null)break; list.add(s); } in.close(); }catch(IOException e){ throw new RuntimeException(e); } return list; } }