Mega Code Archive

 
Categories / Java / Collections Data Structure
 

An extendable Graph datastructure

// $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;   } }