package com.amadornes.framez.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import com.amadornes.framez.util.Graph.INode;
public class Graph<T extends INode> {
private HashMap<T, List<T>> graph;
/**
* Creates a graph from the given HashMap
*
* @param graph
* Contains every vertex as the keys, and every relation of the vertices as the value of each vertex
*/
public Graph(HashMap<T, List<T>> graph) {
this.graph = graph;
}
/**
* Creates a graph from the given list of vertices with no relations
*
* @param vertices
* Initial vertices of the graph
*/
public Graph(List<T> vertices) {
graph = new HashMap<T, List<T>>();
for (T vertex : vertices) {
graph.put(vertex, new ArrayList<T>());
}
}
/**
* Creates an empty graph
*/
public Graph() {
this(new HashMap<T, List<T>>());
}
/**
* Adds a vertex to the graph with no relations.<br>
* If relations want to be added immediately, skip this and use {@link #addEdge(Object, Object)} directly.<br>
* If the vertex exists, nothing happens.
*
* @param vertex
* The vertex to be added
*/
public void addVertex(T vertex) {
if (!graph.containsKey(vertex)) {
graph.put(vertex, new ArrayList<T>());
}
}
/**
* Removes the given vertex and all of its edges
*
* @param vertex
* The vertex to be removed
*/
public void removeVertex(T vertex) {
graph.remove(vertex);
}
/**
* Creates an edge between {@code vertex1} and {@code vertex2}, which are added to the graph if they aren't already there
*
* @param vertex1
* @param vertex2
*/
public void addEdge(T vertex1, T vertex2) {
List<T> edges;
// Add vertex2 to the list of vertex1's relations
edges = getRelations(vertex1);
if (edges == null)
edges = new ArrayList<T>();
if (!edges.contains(vertex2))
edges.add(vertex2);
graph.put(vertex1, edges);
// Add vertex1 to the list of vertex2's relations
edges = getRelations(vertex2);
if (edges == null)
edges = new ArrayList<T>();
if (!edges.contains(vertex1))
edges.add(vertex1);
graph.put(vertex2, edges);
}
/**
* Removes the edge that connects {@code vertex1} and {@code vertex2} if it exists.<br>
* If any of the vertices aren't in the graph, this does nothing.
*
* @param vertex1
* @param vertex2
*/
public void removeEdge(T vertex1, T vertex2) {
if (graph.containsKey(vertex1) && graph.containsKey(vertex2)) {
// Remove v2 from v1's list
List<T> edges = graph.get(vertex1);
if (edges == null)
edges = new ArrayList<T>();
edges.remove(vertex2);
graph.put(vertex1, edges);
// Remove v1 from v2's list
edges = graph.get(vertex2);
if (edges == null)
edges = new ArrayList<T>();
edges.remove(vertex1);
graph.put(vertex2, edges);
}
}
/**
* Returns a set with all the vertices in the graph.
*
* @return vertices
*/
public List<T> getVertices() {
return new ArrayList<T>(graph.keySet());
}
/**
* Returns a list with all the vertices that {@code vertex} is connected with.
*
* @param vertex
* @return vertices
*/
public List<T> getRelations(T vertex) {
addVertex(vertex);
return graph.get(vertex);
}
/**
* True if both vertices exist an there is an edge connecting them, false otherwise.
*
* @param vertex1
* @param vertex2
* @return
*/
public boolean edgeExsists(T vertex1, T vertex2) {
if (!graph.containsKey(vertex1) || !graph.containsKey(vertex2))
return false;
return graph.get(vertex1).contains(vertex2);
}
public Graph<T> getMST(T start) {
List<T> visited = new ArrayList<T>();
Graph<T> result = new Graph<T>();
return deepSearch(start, result, visited);
}
private Graph<T> deepSearch(T start, Graph<T> result, List<T> visited) {
if (!visited.contains(start))
visited.add(start);
result.addVertex(start);
int neighbours = 0;
int maxNeighbours = start.getMaxNeighbors();
List<T> l = new ArrayList<T>();
l.addAll(getRelations(start));
l.removeAll(visited);
Collections.sort(l, new NeighbourSorter<T>(this, visited));
for (T vertex : l) {
if (neighbours >= maxNeighbours)
break;
if (visited.contains(vertex))
continue;
visited.add(vertex);
result.addEdge(start, vertex);
deepSearch(vertex, result, visited);
neighbours++;
}
l.clear();
return result;
}
public static interface INode {
public int getMaxNeighbors();
}
private static class NeighbourSorter<T extends INode> implements Comparator<T> {
private Graph<T> graph;
private List<T> visited;
public NeighbourSorter(Graph<T> graph, List<T> visited) {
this.graph = graph;
this.visited = visited;
}
@Override
public int compare(T a, T b) {
List<T> lA = new ArrayList<T>();
lA.addAll(graph.getRelations(a));
lA.removeAll(visited);
List<T> lB = new ArrayList<T>();
lB.addAll(graph.getRelations(b));
lB.removeAll(visited);
int result = Integer.compare(lA.size(), lB.size()) + Integer.compare(b.getMaxNeighbors(), a.getMaxNeighbors()) * 2;
lA.clear();
lB.clear();
return result;
}
}
}