/*
* Copyright (c) 2003, the JUNG Project and the Regents of the University
* of California
* All rights reserved.
*
* This software is open-source under the BSD license; see either
* "license.txt" or
* http://jung.sourceforge.net/license.txt for a description.
*/
package edu.uci.ics.jung.algorithms.shortestpath;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import edu.uci.ics.jung.graph.Hypergraph;
/**
* Labels each node in the graph according to the BFS distance from the start node(s). If nodes are unreachable, then
* they are assigned a distance of -1.
* All nodes traversed at step k are marked as predecessors of their successors traversed at step k+1.
*
* Running time is: O(m)
* @author Scott White
*/
public class BFSDistanceLabeler {
private Map distanceDecorator = new HashMap();
private List mCurrentList;
private Set mUnvisitedVertices;
private List mVerticesInOrderVisited;
private Map> mPredecessorMap;
/**
* Creates a new BFS labeler for the specified graph and root set
* The distances are stored in the corresponding Vertex objects and are of type MutableInteger
*/
public BFSDistanceLabeler() {
mPredecessorMap = new HashMap>();
}
/**
* Returns the list of vertices visited in order of traversal
* @return the list of vertices
*/
public List getVerticesInOrderVisited() {
return mVerticesInOrderVisited;
}
/**
* Returns the set of all vertices that were not visited
* @return the list of unvisited vertices
*/
public Set getUnvisitedVertices() {
return mUnvisitedVertices;
}
/**
* Given a vertex, returns the shortest distance from any node in the root set to v
* @param v the vertex whose distance is to be retrieved
* @return the shortest distance from any node in the root set to v
*/
public int getDistance(Hypergraph g, V v) {
if (!g.getVertices().contains(v)) {
throw new IllegalArgumentException("Vertex is not contained in the graph.");
}
return distanceDecorator.get(v).intValue();
}
/**
* Returns set of predecessors of the given vertex
* @param v the vertex whose predecessors are to be retrieved
* @return the set of predecessors
*/
public Set getPredecessors(V v) {
return mPredecessorMap.get(v);
}
protected void initialize(Hypergraph g, Set rootSet) {
mVerticesInOrderVisited = new ArrayList();
mUnvisitedVertices = new HashSet();
for(V currentVertex : g.getVertices()) {
mUnvisitedVertices.add(currentVertex);
mPredecessorMap.put(currentVertex,new HashSet());
}
mCurrentList = new ArrayList();
for(V v : rootSet) {
distanceDecorator.put(v, new Integer(0));
mCurrentList.add(v);
mUnvisitedVertices.remove(v);
mVerticesInOrderVisited.add(v);
}
}
private void addPredecessor(V predecessor,V sucessor) {
HashSet predecessors = mPredecessorMap.get(sucessor);
predecessors.add(predecessor);
}
/**
* Computes the distances of all the node from the starting root nodes. If there is more than one root node
* the minimum distance from each root node is used as the designated distance to a given node. Also keeps track
* of the predecessors of each node traversed as well as the order of nodes traversed.
* @param graph the graph to label
* @param rootSet the set of starting vertices to traverse from
*/
public void labelDistances(Hypergraph graph, Set rootSet) {
initialize(graph,rootSet);
int distance = 1;
while (true) {
List newList = new ArrayList();
for(V currentVertex : mCurrentList) {
if(graph.containsVertex(currentVertex)) {
for(V next : graph.getSuccessors(currentVertex)) {
visitNewVertex(currentVertex,next, distance, newList);
}
}
}
if (newList.size() == 0) break;
mCurrentList = newList;
distance++;
}
for(V v : mUnvisitedVertices) {
distanceDecorator.put(v,new Integer(-1));
}
}
/**
* Computes the distances of all the node from the specified root node. Also keeps track
* of the predecessors of each node traversed as well as the order of nodes traversed.
* @param graph the graph to label
* @param root the single starting vertex to traverse from
*/
public void labelDistances(Hypergraph graph, V root) {
labelDistances(graph, Collections.singleton(root));
}
private void visitNewVertex(V predecessor, V neighbor, int distance, List newList) {
if (mUnvisitedVertices.contains(neighbor)) {
distanceDecorator.put(neighbor, new Integer(distance));
newList.add(neighbor);
mVerticesInOrderVisited.add(neighbor);
mUnvisitedVertices.remove(neighbor);
}
int predecessorDistance = distanceDecorator.get(predecessor).intValue();
int successorDistance = distanceDecorator.get(neighbor).intValue();
if (predecessorDistance < successorDistance) {
addPredecessor(predecessor,neighbor);
}
}
/**
* Returns a map from vertices to minimum distances from the original source(s).
* Must be called after {@code labelDistances} in order to contain valid data.
*/
public Map getDistanceDecorator() {
return distanceDecorator;
}
}