/*
* 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.
* <p>
* Running time is: O(m)
*
* @author Scott White
*/
public class BFSDistanceLabeler<V, E> {
private Map<V, Number> distanceDecorator = new HashMap<V, Number>();
private List<V> mCurrentList;
private Set<V> mUnvisitedVertices;
private List<V> mVerticesInOrderVisited;
private Map<V, HashSet<V>> 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<V, HashSet<V>>();
}
/**
* Returns the list of vertices visited in order of traversal
*
* @return the list of vertices
*/
public List<V> getVerticesInOrderVisited() {
return mVerticesInOrderVisited;
}
/**
* Returns the set of all vertices that were not visited
*
* @return the list of unvisited vertices
*/
public Set<V> 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<V, E> 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<V> getPredecessors(V v) {
return mPredecessorMap.get(v);
}
protected void initialize(Hypergraph<V, E> g, Set<V> rootSet) {
mVerticesInOrderVisited = new ArrayList<V>();
mUnvisitedVertices = new HashSet<V>();
for (V currentVertex : g.getVertices()) {
mUnvisitedVertices.add(currentVertex);
mPredecessorMap.put(currentVertex, new HashSet<V>());
}
mCurrentList = new ArrayList<V>();
for (V v : rootSet) {
distanceDecorator.put(v, Integer.valueOf(0));
mCurrentList.add(v);
mUnvisitedVertices.remove(v);
mVerticesInOrderVisited.add(v);
}
}
private void addPredecessor(V predecessor, V sucessor) {
HashSet<V> 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<V, E> graph, Set<V> rootSet) {
initialize(graph, rootSet);
int distance = 1;
while (true) {
List<V> newList = new ArrayList<V>();
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, Integer.valueOf(-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<V, E> graph, V root) {
labelDistances(graph, Collections.singleton(root));
}
private void visitNewVertex(V predecessor, V neighbor, int distance,
List<V> newList) {
if (mUnvisitedVertices.contains(neighbor)) {
distanceDecorator.put(neighbor, Integer.valueOf(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<V, Number> getDistanceDecorator() {
return distanceDecorator;
}
}