Your Progress

11/30 completed

37% complete. Continue learning to master DSA!

Breadth-First Search (BFS) Algorithm

Breadth-First Search (BFS) is a fundamental graph traversal algorithm that explores all vertices at the current depth level before moving to vertices at the next depth level. It's like exploring a maze level by level, ensuring you visit all rooms at one distance before moving further away.

💡 Key Takeaways

  • BFS explores vertices level by level, visiting all neighbors before moving deeper
  • It uses a queue data structure to keep track of vertices to be explored
  • Time complexity is O(V + E), where V is the number of vertices and E is the number of edges
  • BFS finds the shortest path in unweighted graphs
  • BFS is ideal for level-order traversals and finding connected components

How Breadth-First Search Works

Breadth-First Search starts at a specified source vertex and explores all of its neighbors before moving to the next level of vertices. This process continues until all reachable vertices have been visited. The algorithm follows these steps:

  1. Initialization: Start with a source vertex, mark it as visited, and enqueue it.
  2. Exploration: Dequeue a vertex, visit it, and enqueue all its unvisited adjacent vertices.
  3. Repetition: Repeat the exploration step until the queue is empty.

Why Use a Queue?

The queue data structure is essential for BFS because it follows the First-In-First-Out (FIFO) principle, which ensures that vertices are processed in the order they are discovered. This guarantees the level-by-level traversal characteristic of BFS.

Visual Representation

Let's visualize how BFS traverses a simple graph:

BFS Traversal Step-by-Step

A
B
C
D
E
1

Start at vertex A: Mark A as visited and enqueue it.

Queue: [A]

2

Dequeue A: Visit A, then enqueue all its unvisited neighbors (B and C).

Queue: [B, C]

3

Dequeue B: Visit B, then enqueue its unvisited neighbor (D).

Queue: [C, D]

4

Dequeue C: Visit C, then enqueue its unvisited neighbor (E).

Queue: [D, E]

5

Dequeue D and E: Visit D and E. No more unvisited neighbors.

Queue: [ ] (empty)

Final BFS Traversal Order: A, B, C, D, E

BFS Implementation

Let's implement the Breadth-First Search algorithm in various programming languages:

1#include <stdio.h>
2#include <stdlib.h>
3#include <stdbool.h>
4
5// Define the maximum number of vertices
6#define MAX_VERTICES 100
7
8// Graph represented as an adjacency list
9typedef struct {
10 int vertices; // Number of vertices
11 int* adjacencyList[MAX_VERTICES]; // Array of adjacency lists
12 int size[MAX_VERTICES]; // Size of each adjacency list
13 int capacity[MAX_VERTICES]; // Capacity of each adjacency list
14} Graph;
15
16// Queue for BFS traversal
17typedef struct {
18 int items[MAX_VERTICES];
19 int front;
20 int rear;
21} Queue;
22
23// Initialize an empty queue
24Queue* createQueue() {
25 Queue* q = (Queue*)malloc(sizeof(Queue));
26 q->front = -1;
27 q->rear = -1;
28 return q;
29}
30
31// Check if the queue is empty
32bool isEmpty(Queue* q) {
33 return q->front == -1;
34}
35
36// Add element to the queue
37void enqueue(Queue* q, int value) {
38 if (q->rear == MAX_VERTICES - 1)
39 printf("Queue is full\n");
40 else {
41 if (q->front == -1)
42 q->front = 0;
43 q->rear++;
44 q->items[q->rear] = value;
45 }
46}
47
48// Remove element from the queue
49int dequeue(Queue* q) {
50 int item;
51 if (isEmpty(q)) {
52 printf("Queue is empty\n");
53 return -1;
54 } else {
55 item = q->items[q->front];
56 q->front++;
57 if (q->front > q->rear) {
58 q->front = -1;
59 q->rear = -1;
60 }
61 return item;
62 }
63}
64
65// Initialize a graph with n vertices
66Graph* createGraph(int vertices) {
67 Graph* graph = (Graph*)malloc(sizeof(Graph));
68 graph->vertices = vertices;
69
70 for (int i = 0; i < vertices; i++) {
71 graph->adjacencyList[i] = (int*)malloc(sizeof(int));
72 graph->size[i] = 0;
73 graph->capacity[i] = 1;
74 }
75
76 return graph;
77}
78
79// Add an edge to the graph
80void addEdge(Graph* graph, int src, int dest) {
81 // Add edge from src to dest
82 if (graph->size[src] == graph->capacity[src]) {
83 graph->capacity[src] *= 2;
84 graph->adjacencyList[src] = (int*)realloc(graph->adjacencyList[src], graph->capacity[src] * sizeof(int));
85 }
86 graph->adjacencyList[src][graph->size[src]++] = dest;
87
88 // Add edge from dest to src (for undirected graph)
89 if (graph->size[dest] == graph->capacity[dest]) {
90 graph->capacity[dest] *= 2;
91 graph->adjacencyList[dest] = (int*)realloc(graph->adjacencyList[dest], graph->capacity[dest] * sizeof(int));
92 }
93 graph->adjacencyList[dest][graph->size[dest]++] = src;
94}
95
96// BFS traversal of the graph starting from vertex s
97void BFS(Graph* graph, int startVertex) {
98 // Mark all vertices as not visited
99 bool visited[MAX_VERTICES] = {false};
100
101 // Create a queue for BFS
102 Queue* q = createQueue();
103
104 // Mark the current node as visited and enqueue it
105 visited[startVertex] = true;
106 enqueue(q, startVertex);
107
108 printf("BFS traversal starting from vertex %d: ", startVertex);
109
110 while (!isEmpty(q)) {
111 // Dequeue a vertex from queue and print it
112 int currentVertex = dequeue(q);
113 printf("%d ", currentVertex);
114
115 // Get all adjacent vertices of the dequeued vertex
116 // If an adjacent has not been visited, mark it visited and enqueue it
117 for (int i = 0; i < graph->size[currentVertex]; i++) {
118 int adjacentVertex = graph->adjacencyList[currentVertex][i];
119 if (!visited[adjacentVertex]) {
120 visited[adjacentVertex] = true;
121 enqueue(q, adjacentVertex);
122 }
123 }
124 }
125 printf("\n");
126
127 free(q);
128}
129
130// Driver program
131int main() {
132 // Create a graph with 5 vertices
133 Graph* graph = createGraph(5);
134
135 // Add edges (representing the example graph in the visual)
136 addEdge(graph, 0, 1); // A - B
137 addEdge(graph, 0, 2); // A - C
138 addEdge(graph, 1, 3); // B - D
139 addEdge(graph, 2, 4); // C - E
140
141 // Perform BFS starting from vertex 0 (A)
142 BFS(graph, 0);
143
144 // Free allocated memory
145 for (int i = 0; i < graph->vertices; i++) {
146 free(graph->adjacencyList[i]);
147 }
148 free(graph);
149
150 return 0;
151}

Note: The above implementations use an adjacency list to represent the graph. This is generally more efficient than an adjacency matrix for sparse graphs, which is common in real-world scenarios.

Applications of BFS

Breadth-First Search is a versatile algorithm with numerous applications:

Shortest Path (Unweighted)

In unweighted graphs, BFS finds the shortest path between two vertices. Since BFS explores level by level, when it first discovers a vertex, it has found the shortest path to that vertex from the source.

Connected Components

BFS can be used to find all connected components in an undirected graph by running it from each unvisited vertex after the previous BFS traversal completes.

Web Crawlers

Search engines use BFS to index web pages. Starting from a seed URL, they follow links level by level to discover and index new pages.

Social Networks

Finding all friends within a certain degree of connection (e.g., "friends of friends") is a natural application of BFS in social network analysis.

Puzzle Solving

BFS is used in solving puzzles like the Sliding Puzzle or Rubik's Cube, where each state is a vertex and possible moves create edges between states.

Garbage Collection

Some garbage collection algorithms use BFS to identify and collect unreachable objects by traversing the object reference graph.

Time and Space Complexity

Understanding the efficiency of BFS is crucial for its application in real-world problems:

AspectComplexityExplanation
Time ComplexityO(V + E)Each vertex is processed once (O(V)) and each edge is examined once (O(E)) when we process all adjacency lists.
Space ComplexityO(V)We need to store the visited array (O(V)) and the queue can grow up to O(V) in the worst case.

Performance Tip

For large graphs, optimizing the representation can significantly impact performance. Using an adjacency list instead of an adjacency matrix is generally more efficient for sparse graphs, reducing space requirements from O(V²) to O(V + E).

BFS vs DFS: A Comparison

BFS and DFS are both fundamental graph traversal algorithms, but they have different characteristics and use cases:

CharacteristicBreadth-First SearchDepth-First Search
Traversal MethodLevel by levelPath by path (as deep as possible)
Data StructureQueue (FIFO)Stack (LIFO) or Recursion
Space ComplexityUsually higher (proportional to branching factor)Usually lower (proportional to height of tree)
CompleteYes (finds all nodes at current distance before moving further)No (may go very deep in one direction)
Best For
  • Shortest path (unweighted)
  • Level-order traversal
  • Finding all nodes at distance k
  • Finding connected components
  • Maze solving
  • Topological sorting
  • Cycle detection
  • Path finding (any path)
ImplementationUsually iterative with a queueSimpler with recursion

Next Steps

Now that you understand Breadth-First Search, you can explore these related topics:

Related Tutorials

Depth-First Search

Learn about depth-first search, another fundamental graph traversal algorithm.

Learn more

Graphs

Understand graph data structures and their representations.

Learn more

Shortest Path Algorithms

Learn algorithms for finding the shortest path in a graph.

Learn more