Java tutorial
// $Id: Graph.java 87 2010-04-09 02:12:11Z gabe.johnson@gmail.com $ //package org.six11.util.adt; import java.util.List; import java.util.Queue; import java.util.Map; import java.util.HashMap; import java.util.ArrayList; /** * An extendable Graph datastructure. This provides a starting point for a host of graph * datastructure applications, such as scheduling algorithms, decision trees, resource conflict * resolution, etc. It also may make you better looking. * * The code here is a translation of the algorithms described in "Introduction to Algorithms" by * Cormen, Leiserson, and Rivest (aka "Big Red"). I've also added my own special sauce--callbacks (a * la Runnables) that you can use to follow the state of progress. **/ public class Graph { public final static int STATE_NONE = 0; public final static int STATE_BFS = 1; public final static int STATE_DFS = 2; public final static int WHITE = 0; public final static int GRAY = 1; public final static int BLACK = 2; public final static int UNKNOWN = 0; public final static int TREE = 1; public final static int BACK = 2; public final static int FORWARD = 3; public final static int CROSS = 4; // these contain all the nodes and edges protected List<Node> nodes; protected List<Edge> edges; // tells you if the graph edges are directed or undirected protected boolean directed; // search state indicates what the last search was, which tells you // what information you may be able to find out. a STATE_* value. transient protected int searchState = STATE_NONE; // if edgeDataValid is true, this tells you if the graph has ... transient protected boolean cycles; transient protected List<Edge> cross; transient protected boolean forward; transient protected boolean tree; // this tells us if the value of the above edge data vars can be // trusted. if not, we have to directly look at the set of nodes. transient protected boolean edgeDataValid; transient protected List<Node> startNodes; /** * Create a directed graph. */ public Graph() { nodes = new ArrayList<Node>(); edges = new ArrayList<Edge>(); directed = true; } public void setDirected(boolean directed) { this.directed = directed; } public abstract static class NodeCallback { public abstract void run(Node n); } public static class Node { public Object data; private int state = WHITE; private int d = 0; private int f = 0; private Node p; private boolean child; // NodeCallbacks to invoke when entering a state. private Map<Integer, NodeCallback> actions; public Node(Object data) { this.data = data; this.actions = new HashMap<Integer, NodeCallback>(); } protected boolean isVisited() { return state > WHITE; } protected void setState(int state) { if (this.state == state || state < WHITE || state > BLACK) { return; } this.state = state; // Debug.out("Graph", "Changed state of " + this + "#" + hashCode() + " to " + getStateStr()); if (state == WHITE) { d = 0; f = 0; p = null; child = false; } if (actions.get(state) != null) { actions.get(state).run(Node.this); } } public int getState() { return state; } public String getStateStr() { String ret = "UNKNOWN"; switch (state) { case WHITE: ret = "White"; break; case GRAY: ret = "Gray"; break; case BLACK: ret = "Black"; break; } return ret; } public int getDistance() { return d; } public int getDiscovered() { return d; } public int getFinished() { return f; } protected void setDistance(int d) { this.d = d; } protected void setDiscovered(int t) { d = t; } protected void setFinished(int t) { f = t; } public Node getPredecessor() { return p; } protected void setPredecessor(Node p) { this.p = p; // Debug.out("Graph", "Changed parent of " + this + "#" + hashCode() + " to " + p); } protected void setChild() { child = true; } protected boolean isChild() { return child; } protected boolean isAncestor(Node a) { boolean ret = false; if (a != null && p != null) { ret = a.equals(p) || p.isAncestor(a); } return ret; } public void setAction(int state, NodeCallback cb) { actions.put(state, cb); } public boolean equals(Object other) { boolean ret = false; if (other instanceof Node) { Node n = (Node) other; ret = data.equals(n.data); } return ret; } public String toString() { return (data == null ? super.toString() : data.toString()); } } public abstract static class EdgeCallback { public abstract void run(Edge e); } public static class Edge { public Node a; public Node b; public Object data; private int mode; private Map<Integer, EdgeCallback> actions; public Edge(Node a, Node b, Object data) { this.a = a; this.b = b; this.data = data; actions = new HashMap<Integer, EdgeCallback>(); } public void setAction(int state, EdgeCallback cb) { actions.put(state, cb); } public boolean equals(Object other) { boolean ret = false; if (other instanceof Edge) { Edge e = (Edge) other; ret = (a.equals(e.a) && b.equals(e.b) && data.equals(e.data)); } return ret; } protected void setMode(int mode) { if (this.mode != mode) { this.mode = mode; if (actions.get(mode) != null) { actions.get(mode).run(Edge.this); } } } public int getMode() { return mode; } public String getModeStr() { String ret = "UNKNOWN"; switch (mode) { case FORWARD: ret = "Forward"; break; case TREE: ret = "Tree"; break; case BACK: ret = "Back"; break; case CROSS: ret = "Cross"; break; } return ret; } public boolean isKnown() { return mode != UNKNOWN; } public boolean isTree() { return mode == TREE; } public boolean isBack() { return mode == BACK; } public boolean isForward() { return mode == FORWARD; } public boolean isCross() { return mode == CROSS; } public String toString() { String ns = a + "->" + b + " "; return (data == null ? ns + super.toString() : ns + data.toString()); } } public void addNode(Node n) { if (!nodes.contains(n)) { nodes.add(n); clearState(); } } public void removeNode(Node n) { if (nodes.contains(n)) { nodes.remove(n); clearState(); } } /** * Tells you if this graph contains a node whose state and data are both equal (== or equals(..)) * to the given Node. */ public boolean containsNode(Node n) { return nodes.contains(n); } public boolean containsEdge(Edge e) { return edges.contains(e); } public boolean containsEdge(Node a, Node b) { boolean ret = false; for (Edge e : edges) { if (e.a.equals(a) && e.b.equals(b) || (!directed && e.b.equals(a) && e.a.equals(b))) { ret = true; break; } } return ret; } public void addEdge(Edge e) { if (!edges.contains(e)) { edges.add(e); clearState(); } } public void removeEdge(Edge e) { if (edges.contains(e)) { edges.remove(e); clearState(); } } protected void clearState() { searchState = STATE_NONE; edgeDataValid = false; startNodes = null; } public void bfs(Queue<Node> q) { clearState(); for (Node n : nodes) { n.setState(q.contains(n) ? GRAY : WHITE); } for (Edge e : edges) { e.setMode(UNKNOWN); } for (Node n : q) { n.setDistance(0); n.setPredecessor(null); } while (!q.isEmpty()) { Node n = q.remove(); List<Node> out = findNextNodes(n); for (Node m : out) { Edge e = findEdge(n, m); if (e != null) { if (m.getState() == WHITE) { e.setMode(TREE); } else if (m.getState() == GRAY) { e.setMode(BACK); } } if (!m.isVisited()) { m.setDistance(n.getDistance() + 1); m.setPredecessor(n); m.setState(GRAY); q.offer(m); } } n.setState(BLACK); } searchState = STATE_BFS; } public void dfs(List<Node> these) { clearState(); for (Node n : nodes) { n.setState(WHITE); n.setPredecessor(null); } for (Edge e : edges) { e.setMode(UNKNOWN); } int time = 0; time = dfs(null, these, time); searchState = STATE_DFS; } public void dfs() { dfs(nodes); } protected int dfs(Node p, List<Node> out, int time) { for (Node n : out) { Edge e = findEdge(p, n); if (e != null) { n.setChild(); } if (n.getState() == WHITE) { if (e != null) { e.setMode(1); } n.setPredecessor(p); time = dfsVisit(n, ++time); } else if (p != null && n.getState() == GRAY && e != null) { e.setMode(2); } else if (p != null && e != null) { if (n.isAncestor(p)) { e.setMode(3); } else { e.setMode(4); } } else if (e != null && e.getMode() == 0) { System.out.println(" --- ERROR ---------"); System.out.println(" - n.state: " + n.getState()); System.out.println(" - e: " + e); System.out.println(" - p: " + p); System.out.println(" -------------------"); } } return time; } protected int dfsVisit(Node n, int time) { n.setState(GRAY); n.setDiscovered(++time); List<Node> out = findNextNodes(n); dfs(n, out, time); n.setState(BLACK); n.setFinished(++time); return time; } public List<Node> findNextNodes(Node n) { List<Node> ret = new ArrayList<Node>(); for (Edge e : edges) { if (e.a.equals(n)) { ret.add(e.b); } if (!directed && e.b.equals(n)) { ret.add(e.a); } } return ret; } public Edge findEdge(Node a, Node b) { Edge ret = null; if (a != null && b != null) { for (Edge e : edges) { if (e.a.equals(a) && e.b.equals(b)) { ret = e; break; } else if (!directed && e.a.equals(b) && e.b.equals(a)) { ret = e; break; } } } return ret; } public List<Edge> findEdgesEntering(Node n) { List<Edge> ret = new ArrayList<Edge>(); for (Edge e : edges) { if (e.b.equals(n)) { ret.add(e); } } return ret; } public List<Node> getNodes() { return nodes; } public List<Node> getNodes(Object data) { List<Node> ret = new ArrayList<Node>(); for (Node n : nodes) { if (n.data.equals(data)) { ret.add(n); } } return ret; } public List<Edge> getEdges() { return edges; } public List<Edge> getCrossEdges() { computeEdgeData(); return cross; } public List<Node> findStartNodes() { List<Node> ret; if (searchState != STATE_DFS) { dfs(); ret = findStartNodes(); } else { if (startNodes == null) { startNodes = new ArrayList<Node>(); for (Node n : nodes) { if (!n.isChild()) { startNodes.add(n); } } } ret = startNodes; } return ret; } public boolean hasCycles() { computeEdgeData(); return cycles; } public boolean hasForward() { computeEdgeData(); return forward; } public boolean hasCross() { computeEdgeData(); return !cross.isEmpty(); } public boolean hasTree() { computeEdgeData(); return tree; } protected void computeEdgeData() { if (searchState != STATE_DFS) { dfs(); } if (edgeDataValid) return; cycles = tree = forward = false; cross = new ArrayList<Edge>(); for (Edge e : edges) { if (e.isBack()) { cycles = true; } if (e.isTree()) { tree = true; } if (e.isForward()) { forward = true; } if (e.isCross()) { cross.add(e); } } edgeDataValid = true; } }