Your Progress
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:
- Initialization: Start with a source vertex, mark it as visited, and enqueue it.
- Exploration: Dequeue a vertex, visit it, and enqueue all its unvisited adjacent vertices.
- 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
Start at vertex A: Mark A as visited and enqueue it.
Queue: [A]
Dequeue A: Visit A, then enqueue all its unvisited neighbors (B and C).
Queue: [B, C]
Dequeue B: Visit B, then enqueue its unvisited neighbor (D).
Queue: [C, D]
Dequeue C: Visit C, then enqueue its unvisited neighbor (E).
Queue: [D, E]
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>45// Define the maximum number of vertices6#define MAX_VERTICES 10078// Graph represented as an adjacency list9typedef struct {10 int vertices; // Number of vertices11 int* adjacencyList[MAX_VERTICES]; // Array of adjacency lists12 int size[MAX_VERTICES]; // Size of each adjacency list13 int capacity[MAX_VERTICES]; // Capacity of each adjacency list14} Graph;1516// Queue for BFS traversal17typedef struct {18 int items[MAX_VERTICES];19 int front;20 int rear;21} Queue;2223// Initialize an empty queue24Queue* createQueue() {25 Queue* q = (Queue*)malloc(sizeof(Queue));26 q->front = -1;27 q->rear = -1;28 return q;29}3031// Check if the queue is empty32bool isEmpty(Queue* q) {33 return q->front == -1;34}3536// Add element to the queue37void 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}4748// Remove element from the queue49int 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}6465// Initialize a graph with n vertices66Graph* createGraph(int vertices) {67 Graph* graph = (Graph*)malloc(sizeof(Graph));68 graph->vertices = vertices;6970 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 }7576 return graph;77}7879// Add an edge to the graph80void addEdge(Graph* graph, int src, int dest) {81 // Add edge from src to dest82 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;8788 // 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}9596// BFS traversal of the graph starting from vertex s97void BFS(Graph* graph, int startVertex) {98 // Mark all vertices as not visited99 bool visited[MAX_VERTICES] = {false};100101 // Create a queue for BFS102 Queue* q = createQueue();103104 // Mark the current node as visited and enqueue it105 visited[startVertex] = true;106 enqueue(q, startVertex);107108 printf("BFS traversal starting from vertex %d: ", startVertex);109110 while (!isEmpty(q)) {111 // Dequeue a vertex from queue and print it112 int currentVertex = dequeue(q);113 printf("%d ", currentVertex);114115 // Get all adjacent vertices of the dequeued vertex116 // If an adjacent has not been visited, mark it visited and enqueue it117 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");126127 free(q);128}129130// Driver program131int main() {132 // Create a graph with 5 vertices133 Graph* graph = createGraph(5);134135 // Add edges (representing the example graph in the visual)136 addEdge(graph, 0, 1); // A - B137 addEdge(graph, 0, 2); // A - C138 addEdge(graph, 1, 3); // B - D139 addEdge(graph, 2, 4); // C - E140141 // Perform BFS starting from vertex 0 (A)142 BFS(graph, 0);143144 // Free allocated memory145 for (int i = 0; i < graph->vertices; i++) {146 free(graph->adjacencyList[i]);147 }148 free(graph);149150 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:
Aspect | Complexity | Explanation |
---|---|---|
Time Complexity | O(V + E) | Each vertex is processed once (O(V)) and each edge is examined once (O(E)) when we process all adjacency lists. |
Space Complexity | O(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:
Characteristic | Breadth-First Search | Depth-First Search |
---|---|---|
Traversal Method | Level by level | Path by path (as deep as possible) |
Data Structure | Queue (FIFO) | Stack (LIFO) or Recursion |
Space Complexity | Usually higher (proportional to branching factor) | Usually lower (proportional to height of tree) |
Complete | Yes (finds all nodes at current distance before moving further) | No (may go very deep in one direction) |
Best For |
|
|
Implementation | Usually iterative with a queue | Simpler with recursion |
Next Steps
Now that you understand Breadth-First Search, you can explore these related topics:
- Depth-First Search-Another fundamental graph traversal algorithm with different characteristics
- Graph Representations-Learn different ways to represent graphs in programming
- Shortest Path Algorithms-Discover algorithms like Dijkstra's and Bellman-Ford for finding shortest paths in weighted graphs
Related Tutorials
Depth-First Search
Learn about depth-first search, another fundamental graph traversal algorithm.
Learn moreGraphs
Understand graph data structures and their representations.
Learn moreShortest Path Algorithms
Learn algorithms for finding the shortest path in a graph.
Learn more