package net.datastructures; /** * An implementation for a graph structure using an adjacency map for each vertex. * * Every vertex stores an element of type V. * Every edge stores an element of type E. * * @author Michael T. Goodrich * @author Roberto Tamassia * @author Michael H. Goldwasser */ public class AdjacencyMapGraph implements Graph { private boolean isDirected; private PositionalList> vertices = new LinkedPositionalList<>(); private PositionalList> edges = new LinkedPositionalList<>(); /** * Constructs an empty graph. * The parameter determines whether this is an undirected or directed graph. */ public AdjacencyMapGraph(boolean directed) { isDirected = directed; } /** Returns the number of vertices of the graph */ public int numVertices() { return vertices.size(); } /** Returns the vertices of the graph as an iterable collection */ public Iterable> vertices() { return vertices; } /** Returns the number of edges of the graph */ public int numEdges() { return edges.size(); } /** Returns the edges of the graph as an iterable collection */ public Iterable> edges() { return edges; } /** * Returns the number of edges for which vertex v is the origin. * Note that for an undirected graph, this is the same result * returned by inDegree(v). * @throws IllegalArgumentException if v is not a valid vertex */ public int outDegree(Vertex v) throws IllegalArgumentException { InnerVertex vert = validate(v); return vert.getOutgoing().size(); } /** * Returns an iterable collection of edges for which vertex v is the origin. * Note that for an undirected graph, this is the same result * returned by incomingEdges(v). * @throws IllegalArgumentException if v is not a valid vertex */ public Iterable> outgoingEdges(Vertex v) throws IllegalArgumentException { InnerVertex vert = validate(v); return vert.getOutgoing().values(); // edges are the values in the adjacency map } /** * Returns the number of edges for which vertex v is the destination. * Note that for an undirected graph, this is the same result * returned by outDegree(v). * @throws IllegalArgumentException if v is not a valid vertex */ public int inDegree(Vertex v) throws IllegalArgumentException { InnerVertex vert = validate(v); return vert.getIncoming().size(); } /** * Returns an iterable collection of edges for which vertex v is the destination. * Note that for an undirected graph, this is the same result * returned by outgoingEdges(v). * @throws IllegalArgumentException if v is not a valid vertex */ public Iterable> incomingEdges(Vertex v) throws IllegalArgumentException { InnerVertex vert = validate(v); return vert.getIncoming().values(); // edges are the values in the adjacency map } /** Returns the edge from u to v, or null if they are not adjacent. */ public Edge getEdge(Vertex u, Vertex v) throws IllegalArgumentException { InnerVertex origin = validate(u); return origin.getOutgoing().get(v); // will be null if no edge from u to v } /** * Returns the vertices of edge e as an array of length two. * If the graph is directed, the first vertex is the origin, and * the second is the destination. If the graph is undirected, the * order is arbitrary. */ public Vertex[] endVertices(Edge e) throws IllegalArgumentException { InnerEdge edge = validate(e); return edge.getEndpoints(); } /** Returns the vertex that is opposite vertex v on edge e. */ public Vertex opposite(Vertex v, Edge e) throws IllegalArgumentException { InnerEdge edge = validate(e); Vertex[] endpoints = edge.getEndpoints(); if (endpoints[0] == v) return endpoints[1]; else if (endpoints[1] == v) return endpoints[0]; else throw new IllegalArgumentException("v is not incident to this edge"); } /** Inserts and returns a new vertex with the given element. */ public Vertex insertVertex(V element) { InnerVertex v = new InnerVertex<>(element, isDirected); v.setPosition(vertices.addLast(v)); return v; } /** * Inserts and returns a new edge between vertices u and v, storing given element. * * @throws IllegalArgumentException if u or v are invalid vertices, or if an edge already exists between u and v. */ public Edge insertEdge(Vertex u, Vertex v, E element) throws IllegalArgumentException { if (getEdge(u,v) == null) { InnerEdge e = new InnerEdge<>(u, v, element); e.setPosition(edges.addLast(e)); InnerVertex origin = validate(u); InnerVertex dest = validate(v); origin.getOutgoing().put(v, e); dest.getIncoming().put(u, e); return e; } else throw new IllegalArgumentException("Edge from u to v exists"); } /** Removes a vertex and all its incident edges from the graph. */ public void removeVertex(Vertex v) throws IllegalArgumentException { InnerVertex vert = validate(v); // remove all incident edges from the graph for (Edge e : vert.getOutgoing().values()) removeEdge(e); for (Edge e : vert.getIncoming().values()) removeEdge(e); // remove this vertex from the list of vertices vertices.remove(vert.getPosition()); vert.setPosition(null); // invalidates the vertex } @SuppressWarnings({"unchecked"}) /** Removes an edge from the graph. */ public void removeEdge(Edge e) throws IllegalArgumentException { InnerEdge edge = validate(e); // remove this edge from vertices' adjacencies // Modification by Scot Drysdale to eliminate a casting exception on // the following line: // InnerVertex[] verts = (InnerVertex[]) edge.getEndpoints(); Vertex[] verts = edge.getEndpoints(); ((InnerVertex)verts[0]).getOutgoing().remove(verts[1]); ((InnerVertex)verts[1]).getIncoming().remove(verts[0]); // remove this edge from the list of edges edges.remove(edge.getPosition()); edge.setPosition(null); // invalidates the edge } @SuppressWarnings({"unchecked"}) private InnerVertex validate(Vertex v) { if (!(v instanceof InnerVertex)) throw new IllegalArgumentException("Invalid vertex"); InnerVertex vert = (InnerVertex) v; // safe cast if (!vert.validate(this)) throw new IllegalArgumentException("Invalid vertex"); return vert; } @SuppressWarnings({"unchecked"}) private InnerEdge validate(Edge e) { if (!(e instanceof InnerEdge)) throw new IllegalArgumentException("Invalid edge"); InnerEdge edge = (InnerEdge) e; // safe cast if (!edge.validate(this)) throw new IllegalArgumentException("Invalid edge"); return edge; } //---------------- nested Vertex class ---------------- /** A vertex of an adjacency map graph representation. */ private class InnerVertex implements Vertex { private V element; private Position> pos; private Map, Edge> outgoing, incoming; /** Constructs a new InnerVertex instance storing the given element. */ public InnerVertex(V elem, boolean graphIsDirected) { element = elem; outgoing = new ProbeHashMap<>(); if (graphIsDirected) incoming = new ProbeHashMap<>(); else incoming = outgoing; // if undirected, alias outgoing map } /** Validates that this vertex instance belongs to the given graph. */ public boolean validate(Graph graph) { return (AdjacencyMapGraph.this == graph && pos != null); } /** Returns the element associated with the vertex. */ public V getElement() { return element; } /** Stores the position of this vertex within the graph's vertex list. */ public void setPosition(Position> p) { pos = p; } /** Returns the position of this vertex within the graph's vertex list. */ public Position> getPosition() { return pos; } /** Returns reference to the underlying map of outgoing edges. */ public Map, Edge> getOutgoing() { return outgoing; } /** Returns reference to the underlying map of incoming edges. */ public Map, Edge> getIncoming() { return incoming; } } //------------ end of InnerVertex class ------------ //---------------- nested InnerEdge class ---------------- /** An edge between two vertices. */ private class InnerEdge implements Edge { private E element; private Position> pos; private Vertex[] endpoints; @SuppressWarnings({"unchecked"}) /** Constructs InnerEdge instance from u to v, storing the given element. */ public InnerEdge(Vertex u, Vertex v, E elem) { element = elem; endpoints = (Vertex[]) new Vertex[]{u,v}; // array of length 2 } /** Returns the element associated with the edge. */ public E getElement() { return element; } /** Returns reference to the endpoint array. */ public Vertex[] getEndpoints() { return endpoints; } /** Validates that this edge instance belongs to the given graph. */ public boolean validate(Graph graph) { return AdjacencyMapGraph.this == graph && pos != null; } /** Stores the position of this edge within the graph's vertex list. */ public void setPosition(Position> p) { pos = p; } /** Returns the position of this edge within the graph's vertex list. */ public Position> getPosition() { return pos; } } //------------ end of InnerEdge class ------------ /** * Returns a string representation of the graph. * This is used only for debugging; do not rely on the string representation. */ public String toString() { StringBuilder sb = new StringBuilder(); // sb.append("Edges:"); // for (Edge e : edges) { // Vertex[] verts = endVertices(e); // sb.append(String.format(" (%s->%s, %s)", verts[0].getElement(), verts[1].getElement(), e.getElement())); // } // sb.append("\n"); for (Vertex v : vertices) { sb.append("Vertex " + v.getElement() + "\n"); if (isDirected) sb.append(" [outgoing]"); sb.append(" " + outDegree(v) + " adjacencies:"); for (Edge e: outgoingEdges(v)) sb.append(String.format(" (%s, %s)", opposite(v,e).getElement(), e.getElement())); sb.append("\n"); if (isDirected) { sb.append(" [incoming]"); sb.append(" " + inDegree(v) + " adjacencies:"); for (Edge e: incomingEdges(v)) sb.append(String.format(" (%s, %s)", opposite(v,e).getElement(), e.getElement())); sb.append("\n"); } } return sb.toString(); } }