In our last lesson, we explored Depth-First Search, a traversal strategy that plunges as deeply as possible into a graph before backtracking. Now, we turn our attention to its equally important counterpart: Breadth-First Search (BFS). Where DFS explores "deep," BFS explores "wide." It systematically visits vertices level by level, starting from a source node.
This lesson focuses on implementing BFS iteratively using one of your familiar data structures, the queue. Understanding this algorithm is crucial, as it's not just another way to visit nodes; its level-by-level approach gives it a remarkable property that we will prove in the next lesson—the ability to find the shortest path in an unweighted graph.
The Philosophy of BFS: Exploring Level by Level
Imagine dropping a stone into a still pond. The ripples spread out in concentric circles, moving uniformly outward from the center. Breadth-First Search explores a graph in a similar manner. Starting from a source vertex s, it first visits all vertices at distance 1 from s. Then, it visits all vertices at distance 2, and so on, until all reachable vertices have been explored.
This concept of "levels" or "distance" is key. In an unweighted graph, the distance between two vertices is simply the minimum number of edges on a path connecting them.
- Level 0: The source vertex
s. - Level 1: All direct neighbors of
s. - Level 2: All unvisited neighbors of Level 1 vertices.
- Level 3: All unvisited neighbors of Level 2 vertices.
- ...and so on.
The video below from the "take U forward" channel provides an excellent visual demonstration of this level-wise traversal.
G-5. Breadth-First Search (BFS) | C++ and Java | Traversal Technique in Graphs
This video introduces the core idea of BFS as a level-wise traversal.
Watch the initial segment from the beginning of the explanation to the end of the second example. Pay close attention to how the "levels" are defined based on the distance from the starting node and how all nodes at a given level are visited before moving to the next.
The Queue: The Engine of BFS
In the last lesson, we saw how the implicit stack of recursion was a natural fit for the backtracking nature of DFS. For the "first-in, first-out" level-wise exploration of BFS, the perfect data structure is the queue.
A queue ensures that we process vertices in the exact order we discover them. By adding the source to the queue, and then systematically dequeuing a vertex and enqueuing all its unvisited neighbors, we guarantee that we fully explore each level before any vertex from the next level is even considered.
The process is as follows:
- Initialize a queue and add the starting vertex
sto it. - Create a
visitedarray (just as with DFS) and marksas visited. - Loop as long as the queue is not empty:
a. Dequeue a vertex, let's call itu.
b. Processu(e.g., add it to our traversal list).
c. For each neighborvofu:
i. Ifvhas not been visited, mark it as visited and enqueue it.
This animated GIF provides a clear visual of this process, showing how nodes are added to the back of the "Frontier Queue" and processed from the front, building the traversal level by level.

The Algorithm in Action
Let's trace the algorithm on a more general graph. The image below provides a comprehensive snapshot of a BFS traversal starting from vertex 'a'. It shows the state of the graph, the adjacency list, the queue, the visited set, and the final traversal order.

To formalize this, cp-algorithms.com provides a concise description.
This resource describes the BFS algorithm formally.
Read the section titled "Description of the algorithm". It uses the analogy of a "fire spreading" which is another great way to visualize the level-by-level expansion. It also introduces the idea of tracking distances and parent pointers, which we'll explore soon.
Implementing BFS in C++
Translating this algorithm into C++ is straightforward, especially since you are already familiar with the adjacency list representation and the visited array from our DFS lesson. We'll use std::vector for the adjacency list and visited array, and std::queue from the <queue> header for our queue.
The core implementation is very clean and directly mirrors the logic we've discussed.
This section provides a canonical C++ implementation of BFS.
Focus on the C++ code snippet. Notice the while (!q.empty()) loop, the q.front() and q.pop() operations, and the check if (!used[u]) before adding a neighbor to the queue.
Let's break down a slightly simplified version of that code which only performs the traversal.
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
// Assumes 'adj' is a pre-filled adjacency list
vector<int> bfs(int start_node, int n, const vector<vector<int>>& adj) {
vector<int> traversal_order;
vector<bool> visited(n + 1, false); // Using n+1 for 1-based indexing
queue<int> q;
// 1. Initial setup
q.push(start_node);
visited[start_node] = true;
// 2. Loop while there are nodes to process
while (!q.empty()) {
// 3. Dequeue a vertex and add to traversal list
int u = q.front();
q.pop();
traversal_order.push_back(u);
// 4. Enqueue all unvisited neighbors
for (int v : adj[u]) {
if (!visited[v]) {
visited[v] = true;
q.push(v);
}
}
}
return traversal_order;
}
To see a line-by-line trace of this code on an example graph, the "take U forward" channel provides another excellent walkthrough.
G-5. Breadth-First Search (BFS) | C++ and Java | Traversal Technique in Graphs
This video provides a step-by-step trace of the BFS algorithm and its C++ implementation.
First, watch the walkthrough of the algorithm's logic, which shows how the queue and visited array are updated at each step. Then, review the C++ implementation to see how this logic translates directly into code.
Complexity Analysis
The performance analysis for BFS is identical to that of DFS when using an adjacency list.
-
Time Complexity: . Each vertex is enqueued and dequeued exactly once. When a vertex
uis dequeued, we iterate through its neighbors. Over the entire run of the algorithm, this means we look at each edge(u,v)once when processinguand once when processingv(in an undirected graph). Thus, the total work is proportional to the sum of vertices and edges. -
Space Complexity: . This is dominated by the storage for the adjacency list. The auxiliary space required by the
visitedarray is , and thequeuecan, in the worst case (a very "wide" graph, like a star graph), hold up to vertices.
This linear time complexity makes BFS, like DFS, a highly efficient and fundamental tool for competitive programming.
Conclusion
In this lesson, you have learned the "breadth-first" approach to graph traversal. You now understand how its level-by-level exploration is naturally implemented with a queue, a FIFO data structure.
Key Takeaways:
- BFS explores "wide": It visits all nodes at a certain distance (level) before moving to the next level.
- A queue is the right tool: The FIFO nature of a queue ensures nodes are processed in the order they are discovered, facilitating level-wise traversal.
- The iterative implementation is simple: Using a
whileloop, a queue, and avisitedarray, the algorithm is concise and efficient. - The complexity is linear: BFS runs in time and space, making it very fast.
You now have two powerful traversal algorithms in your toolkit: the deep, recursive DFS and the wide, iterative BFS. In our next lesson, we will formally prove the most celebrated property of BFS: why it is guaranteed to find the shortest path between two nodes in any unweighted graph.