Breadth first search is a core algorithm used to traverse trees that can be generalized to traversing graphs as well.

Breadth first search might remind you of level-order traversal in a tree, and is also very similar to how graph traversal was done in CPSC 110. The idea is to visit all nodes from a certain distance of the current node by putting them in a queue before marking nodes as visited and iterating over the queue.

Implementation

An example of the BFS algorithm for an adjacency list implemented graph could be something like the following (note that this requires the omitted implementation of an adjacency list graph, a node with parent and value pointers, and a state variable):

typedef enum = {EXPLORED, NEW} STATE;
 
Node* BFS(Graph g, Node* root, int targetValue){
	Queue q;
	root->state = STATE.EXPLORED;
	q.enque(root);
	
	while(!q.isEmpty()){
		Node* n = q.dequeue();
		if(n->value == targetValue){
			return n;
		}
		
		for(Node* adjNode : g.getAdjacentNodes(n)){ //for each adjacent node
			if(adjNode->state != EXPLORED){
				adjNode->state = EXPLORED;
				adjNode->parent = n;
				q.enqueue(adjNode);
			}
		}	
	}
}

The idea is relatively simple, we take an arbitrary starting point in a graph (or in the tree’s case, the root) and we queue each adjacent node to the current one into a FIFO queue. We then dequeue nodes from that queue until its empty. Each time we visit a node, we mark it as visited so it doesn’t accidentally get visited multiple times and create a cycle. And that’s pretty much it.

This guarantees visiting every node in a tree or a connected graph, and is relatively easy to implement.

Edge Types

When using BFS, because we need a way to avoid cycles, the general method is to label nodes or edges as visited to avoid requeuing them to the BFS worklist. This can work with nodes or edges interchangeably and there really isn’t that much of a difference. In this class however, we generally do so with edges and it’s pretty common to be asked what kind of edge you will end up with as a result of a BFS algorithm. BFS results in two edge types.

Discovery

Pretty self explanatory, if the edge connects the current node with an unexplored node, it is a discovery edge, an edge that has been ‘discovered’ to be part of the spanning graph.

Cross

A cross edge is basically the name given to any edge that isn’t a discovery edge. The idea is that it ‘crosses’ from one discovery path to another, and doesn’t contribute to discovering new nodes.

Example

For example, see if you can trace the BFS algorithm on the following graph represented by an adjacency list, starting at the vertex A.

Traversal time Complexity

Let be the number of nodes and be the number of edges.

When the breadth first search is ran on a graph represented by an adjacency list, the time complexity is:

Note that if the graph is represented by an adjacency matrix, then the time complexity of the traversal is

instead.

Applications

Tree Detection

When running BFS, you can determine that a graph is a tree if it has no cross edges, all vertices have one parent, and all the vertices are in one connected graph.

Finding a Spanning Tree

If using an edge-based implementation, BFS also naturally forms a spanning tree, which you can return by either constructing a path while you’re traversing or by marking the edges with some kind of state.

Shortest Path Problem

If each time we visit a node, we record their parent (like in our implementation example), we can backtrack a target node to the node we started with, which will give us the shortest path to that node. In effect, breadth first search is a valid shortest path finding algorithm specifically for unweighted, undirected graphs.