Sinken

inf102 oblig2

Oct 25th, 2020
113
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 7.86 KB | None | 0 0
  1. package inf102.h20.student;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.Collections;
  5. import java.util.Comparator;
  6. import java.util.HashMap;
  7. import java.util.LinkedList;
  8. import java.util.List;
  9. import java.util.PriorityQueue;
  10.  
  11. import inf102.h20.graph.Edge;
  12. import inf102.h20.graph.Graph;
  13. import inf102.h20.graph.WeightedGraph;
  14.  
  15. public class ProblemSolver implements IProblem {
  16.  
  17.  
  18.  
  19.     @Override
  20.     /**
  21.      * Given a weigthed graph, this returns an ArrayList of the MST.
  22.      *
  23.      * Time complexity: If V > E+1, we don't have a tree. If V-1 == E, we already have a MST. Hence, we can assume
  24.      * that we will always have E >> V. Thus, time complexity is O(E log E) from adding things to the PriorityQueue
  25.      */
  26.     public <T, E extends Comparable<E>> ArrayList<Edge<T>> mst(WeightedGraph<T, E> g) {
  27.         //This has to be an ArrayList, because that's what we're expected to return
  28.         ArrayList<Edge<T>> mstEdges = new ArrayList<Edge<T>>();            
  29.        
  30.        
  31.         //Instead of a PriorityQueue we could use an ArrayList. Because we only add things once,
  32.         //we could sort the ArrayList in the reverse order, and then always remove the last element
  33.         //to retain the O(1) remove complexity. We would still end up with V log V time complexity.
  34.         //Using a PQ does sound more intuitive though.
  35.         //#TODO isn't arraylist faster? Just O(V)
  36.         PriorityQueue<Edge<T>> pq = new PriorityQueue<Edge<T>>(new Comparator<Edge<T>>() {
  37.             public int compare(Edge<T> e1, Edge<T> e2) {
  38.                 return g.getWeight(e1).compareTo(g.getWeight(e2));
  39.             }});           
  40.        
  41.         UnionFind<T> UF = new UnionFind<T>();                   //Stole this from some dude
  42.        
  43.         for (T V: g.vertices()) {
  44.             UF.add(V, V);                                       //O(V)
  45.         }
  46.         for (Edge<T> e: g.edges()) {                            //O(E)
  47.             pq.add(e);                                          //O(log E)
  48.         }
  49.        
  50.        
  51.         while (!pq.isEmpty() && mstEdges.size() < g.numVertices()-1) {  //O(V)
  52.             Edge<T> e = pq.poll();                                      //O(1)
  53.             T v = e.a;
  54.             T w = e.b;
  55.             if (UF.compressFind(v) == UF.compressFind(w)) {             //O(α(n))
  56.                 continue;
  57.             }
  58.             UF.union(w, v);                                             //O(α(n))
  59.             mstEdges.add(e);                                            //O(1)
  60.         }
  61.         return mstEdges;
  62.     }
  63.    
  64.     /**
  65.      *
  66.      * This is a modified version of https://gist.github.com/autolab2013/8b6035bf014a3f15cdf2
  67.      *
  68.      * The time complexity of both union and compessFind should be O(α(n)), and add should be O(1)
  69.      *
  70.      * @author https://gist.github.com/autolab2013
  71.      *
  72.      * @param <T>
  73.      */
  74.     class UnionFind<T> {
  75.         private HashMap<T, T> parent;
  76.        
  77.         public UnionFind() {
  78.             parent = new HashMap<>();
  79.         }
  80.        
  81.         public void add(T n, T p) {
  82.             parent.put(n, p);           //O(1)
  83.         }
  84.        
  85.         public void union(T n1, T n2) {
  86.             T p1 = compressFind(n1);
  87.             T p2 = compressFind(n2);
  88.             if(p1 != p2) {
  89.                 parent.put(p1, p2);                 //O(1)
  90.             }
  91.         }
  92.        
  93.         public T compressFind(T n) {                //It's hard to say, the book just says very very nearly amortized O(1), but
  94.             T p = parent.get(n);                    //I'll trust wikipedia who says O(α(n)), which is apparently the inverse Ackermann function.
  95.             while(p != parent.get(p)) {             //Either way it's really small.
  96.                 p = parent.get(p);
  97.             }
  98.             T tmp = null;
  99.             T father = parent.get(n);
  100.             while(father != parent.get(father)) {
  101.                 tmp = parent.get(father);
  102.                 parent.put(father, p);
  103.                 father = tmp;
  104.             }
  105.             parent.put(n, p);
  106.             return p;
  107.         }
  108.     }
  109.  
  110.    
  111.  
  112.     @Override
  113.     /**
  114.      * Time complexity: O(V)
  115.      */
  116.     public <T> T lca(Graph<T> g, T root, T u, T v) {
  117.         List<T> pathU = path(g, root, u, root); //O(V)
  118.         List<T> pathV = path(g, root, v, root); //O(V)
  119.         Collections.reverse(pathU);             //O(V)
  120.         Collections.reverse(pathV);             //O(V)
  121.        
  122.         int lcaIndex = 0;
  123.         for (int i = 0; i < Math.min(pathU.size(), pathV.size()); i++) { //O(V)
  124.             if (pathU.get(i).equals(pathV.get(i))) {                     //O(1)
  125.                 lcaIndex = i;
  126.             }
  127.             else break;
  128.         }
  129.        
  130.         return pathU.get(lcaIndex);
  131.     }
  132.    
  133.     /**
  134.      * Not an optimal algorithm...
  135.      * Time complexity: O(V)
  136.      * @param <T>
  137.      * @param g
  138.      * @param root
  139.      * @param u
  140.      * @param last
  141.      * @return
  142.      */
  143.     public <T> List<T> path(Graph<T> g, T root, T u, T last) {
  144.         HashMap<T, T> edgeTo = new HashMap<T, T>();
  145.         HashMap<T, Boolean> marked = new HashMap<T, Boolean>();
  146.        
  147.         for (T V: g.vertices()) {   //Collections.fill              //O(V)
  148.             marked.put(V, false);
  149.         }
  150.        
  151.         LinkedList<T> verticies = new LinkedList<T>();
  152.        
  153.         verticies.add(root);                                        //O(1)
  154.         marked.put(root, true);
  155.        
  156.         //Struggled a bit with finding out this one. At first glance it seems like the while loop is O(V), and
  157.         //then the for loop is also O(V), making this O(V^2). However, there is a relationship between the
  158.         //number of times the while loop will run and the number of times the for loop will run. If you have a tree
  159.         //where every node (except the first and last) have a degree of 2, then the while loop will run V times, and the
  160.         //for loop will only be O(1). The other case of the root having degree V-1 means that the while loop will be
  161.         //O(1) and the for loop will be O(V).
  162.        
  163.         //Either way, the solution is to think logically. We only visit each node once, and we check each edge twice
  164.         //This gives us O(2E+V). Since E = V-1 in the case of a tree, we have O(3V) = O(V)
  165.        
  166.         //O(V)
  167.         while(!verticies.isEmpty()) {                              
  168.             T vertex = verticies.remove();
  169.             for (T V: g.neighbours(vertex)) {                      
  170.                 if (!marked.get(V) && V != last) {
  171.                     edgeTo.put(V, vertex);
  172.                     marked.put(V, true);
  173.                     verticies.add(V);                               //O(log 1)
  174.                 }
  175.             }
  176.             if (vertex == u) break; //No point in traversing the whole tree if we already found the power outage.
  177.         }
  178.         List<T> path = new ArrayList<T>();
  179.         for (T x = u; !x.equals(root); x = edgeTo.get(x)) path.add(x);  //O(V)
  180.         path.add(root);
  181.         return path;
  182.     }
  183.    
  184.  
  185.  
  186.     @Override
  187.     public <T> Edge<T> addRedundant(Graph<T> tree, T root) {
  188.         HashMap<T, T> edgeTo = new HashMap<T, T>();
  189.         //Originally I filled this HashMap with all the verticies in g
  190.         //and filled it with false, so that I could check if it was
  191.         //true or false. However, simply checking if the key exists
  192.         //and then adding the key (and setting it to true as that seem
  193.         //to make sense) if it didn't does save a fair few iterations.
  194.         //The time complexity remains the same, but you save O(V) iterations
  195.         //on not having to fill it, and you save a few during the last step
  196.         //where you count the number of trues..
  197.         //#TODO put this in svar.md instead
  198.         HashMap<T, Integer> count = new HashMap<T, Integer>();
  199.        
  200.         LinkedList<T> verticies = new LinkedList<T>();
  201.        
  202.         verticies.add(root);
  203.         count.put(root, 0);
  204.        
  205.         while(!verticies.isEmpty()) {                   //O(V)
  206.             T vertex = verticies.remove();
  207.             for (T V: tree.neighbours(vertex)) {                   
  208.                 if (!count.containsKey(V)) {
  209.                     edgeTo.put(V, vertex);
  210.                     count.put(V, 1);
  211.                     verticies.add(V);
  212.                 }}}
  213.        
  214.         for (T V: count.keySet()) {
  215.                 for (T x = V; !x.equals(root); x = edgeTo.get(x)) count.put(x, count.get(x)+1);
  216.         }
  217.         T vertex = root;
  218.         while (tree.degree(vertex) < 2) {
  219.             for (T V: tree.neighbours(vertex)) {
  220.                 if (count.get(V) > 0) {
  221.                     count.put(V, 0);
  222.                     vertex = V;
  223.                 }}}
  224.        
  225.         if (vertex != root) return new Edge<T>(vertex, root);
  226.         List<T> vertexToJoin = new ArrayList<T>();
  227.         for (int i = 0; i < 2; i++) {
  228.             vertex = root;
  229.             while (tree.degree(vertex) > 1) {
  230.                 int max = -1;                   //Apparently these should be declared in the smallest possible scope?
  231.                 count.put(vertex, 0);           //Also somehow didn't manage to make this work with the 'last'
  232.                 for (T V: tree.neighbours(vertex)) {
  233.                     if (count.get(V) > max) {
  234.                         vertex = V;
  235.                         max = count.get(V);
  236.                     }}}
  237.             vertexToJoin.add(vertex);
  238.         }
  239.         return new Edge<T>(vertexToJoin.get(0), vertexToJoin.get(1));
  240.     }
  241. }
Add Comment
Please, Sign In to add comment