package uk.ac.rhul.cs.graph; import java.util.Iterator; import com.sosnoski.util.hashset.IntHashSet; import com.sosnoski.util.queue.IntQueue; /** * Iterator that traverses the vertices of a graph in breadth first order. * * @author tamas * */ public class BreadthFirstSearchIterator implements Iterator<Integer> { /** Graph which the iterator will traverse */ protected Graph graph = null; /** * When the BFS is restricted to a subgraph, this node contains the set of nodes that * are allowed during the BFS. Otherwise it is null. */ protected IntHashSet allowedNodes = null; /** Queue that holds the nodes that are to be visited and their distances */ protected IntQueue q = new IntQueue(); /** Set that holds the nodes that have already been visited */ protected IntHashSet visited = new IntHashSet(); /** Distance of the last returned node from the seed */ protected int distance = -1; /** * Constructs a new BFS iterator. * * @param graph the graph to be traversed * @param seedNode the index of the seed node */ public BreadthFirstSearchIterator(Graph graph, int seedNode) { this(graph, seedNode, null); } /** * Constructs a new BFS iterator restricted to a set of nodes. * * @param graph the graph to be traversed * @param seedNode the index of the seed node * @param subset an array of node indices which must be traversed. * Nodes not in this nodeset are assumed to have been * already visited by the iterator. Can also be null, * which means that every node can be traversed. */ public BreadthFirstSearchIterator(Graph graph, int seedNode, int[] subset) { this.graph = graph; if (subset != null) { restrictToSubgraph(subset); } pushNode(seedNode, 0); } /** * Returns the distance of the last returned node from the seed. * * @return the distance of the last node from the seed or -1 if the * traversal has not yet started. */ public int getDistance() { return distance; } /** * Returns whether there are more nodes left in the traversal. */ public boolean hasNext() { return !q.isEmpty(); } /** * Returns the index of the next visited node */ public Integer next() { int result = q.remove(); distance = q.remove(); int[] neighbors = graph.getAdjacentNodeIndicesArray(result, Directedness.OUT); /* Check all the neighbors and add the nodes not visited to the queue */ for (int neighbor: neighbors) { if (!visited.contains(neighbor)) { pushNode(neighbor, distance + 1); } } return result; } /** * Removal is not supported. */ public void remove() { throw new UnsupportedOperationException(); } /** * Restricts the BFS traversal to the given subset. * * @param subset an array of node indices to which we restrict * the traversal */ public void restrictToSubgraph(int[] subset) { allowedNodes = new IntHashSet(); for (int node: subset) { allowedNodes.add(node); } } /** * Pushes the given node and the given distance into the queue if we are allowed * to visit the node. * * @param node the node to push to the queue * @param distance the distance of the node from the start point */ private void pushNode(int node, int distance) { if (allowedNodes != null && !allowedNodes.contains(node)) return; q.add(node); q.add(distance); visited.add(node); } }