GeeksforGeeks Easy

BFS of Graph

BFS of Graph is a GFG problem (Easy). You are given a connected undirected graph as an adjacency list adj, where adj[i] lists the neighbours of vertex i. Return the breadth-first traversal of the graph, starting from vertex 0.

  • Visit the neighbours of a vertex in the order adj[i] lists them, left to right.
  • The graph can have cycles, so a vertex may be reachable along several edges. Each vertex appears in the answer once.

With up to 10⁴ vertices, the traversal has to look at each vertex and edge only a constant number of times: O(V + E).

Constraints
  • 1 <= adj.size() <= 10⁴
  • 1 <= adj[i][j] <= 10⁴
  • The graph is connected and undirected
graphbfstraversal
Open on GeeksforGeeks ↗
02

Intuition

Breadth-first means visiting the graph in rings around vertex 0: the start, then every vertex one edge away, then every vertex two edges away.

A queue produces exactly that order. New vertices join at the back and leave from the front, so every vertex of one ring comes out before any vertex of the next. Doing BFS using a queue needs just one more rule: the graph can have cycles, so mark each vertex the moment it joins the queue, and it can never be added twice.

How to spot this pattern

Reach for BFS in graph problems that ask about distance counted in edges: the fewest moves, the nearest cell, the shortest chain of words. On an unweighted graph BFS takes vertices out of the queue in order of their distance from the start, so the first time it reaches a target is along a shortest path. Rotting Oranges, 01 Matrix and Word Ladder are this same BFS graph traversal with a different rule for what counts as a neighbour.

03

Approach

Try it first

Before reading on: in the second example ([[1,2],[0,3],[0,3,4],[1,2],[2]]), vertex 3 is a neighbour of both 1 and 2. Write down the queue after each vertex is processed. At which moment must 3 be marked so that it enters the queue only once?

1

Start the queue at vertex 0

Mark vertex 0 visited and put it in the queue. It is marked now, before any edge is read, so no neighbour can add it back later.

2

Take the vertex at the front

Remove the front vertex u with popleft() and append it to the answer. The queue hands vertices back in the order they were found, nearest first, so this is where u gets its place in the breadth-first order.

3

Queue each new neighbour

Walk adj[u] from left to right. For each neighbour v:

  • not visited yet: mark it and append it to the back of the queue;
  • already visited: skip it, since it is already waiting in the queue or already in the answer.

Marking at enqueue time is what keeps a vertex with two edges into it from being queued twice.

4

Stop when the queue is empty

An empty queue means every vertex reachable from 0 has been taken out once. Each vertex enters the queue once and each edge is checked from both ends, so the BFS of graph runs in O(V + E) time and O(V) extra space.

04

BFS of Graph solution in Python | C++ | Java

▶1from collections import deque
▶2 
▶3class Solution:
▶4 def bfs(self, adj):
▶5 visited = [False] * len(adj)
▶6 visited[0] = True
▶7 q = deque([0])
▶8 order = []
▶9 while q:
▶10 u = q.popleft()
▶11 order.append(u)
▶12 for v in adj[u]:
▶13 if not visited[v]:
▶14 visited[v] = True
▶15 q.append(v)
▶16 return order
queueddone01234visited✓01234queue0frontansweremptymark 0, queue it
queue[0]front on the left
visited{0}marked as it is queued
Start at vertex 0. It is marked visited the moment it enters the queue, so no neighbour can add it a second time when the search later looks at the same edge from the other end.
queueddone01234visited✓01234queueemptyanswer0take 0 from the front
u0front of the queue
answer[0]1 of 5
queue[]nothing else waiting
Take 0 off the front and add it to the answer. Now its neighbours, in the order adj[0] lists them, are the next ring of the search.
queueddone01234visited✓01✓234queue2frontanswer02 is new → mark it, queue it
edge0 – 2neighbour of 0
visited[2]trueset now, at enqueue
queue[2]joins the back
2 has not been seen, so mark it and put it at the back of the queue. It waits behind nothing, so it is next.
queueddone01234visited✓01✓2✓34queue23frontanswer03 is new → mark it, queue it
edge0 – 3neighbour of 0
visited[3]trueset now, at enqueue
queue[2, 3]joins the back
3 has not been seen, so mark it and put it at the back of the queue. It waits behind 2, which was found earlier and is no farther from 0.
queueddone01234visited✓0✓1✓2✓34queue231frontanswer01 is new → mark it, queue it
edge0 – 1neighbour of 0
visited[1]trueset now, at enqueue
queue[2, 3, 1]joins the back
1 has not been seen, so mark it and put it at the back of the queue. It waits behind 2, 3, which were found earlier and are no farther from 0.
queueddone01234visited✓0✓1✓2✓34queue31frontanswer02take 2 from the front
u2front of the queue
answer[0, 2]2 of 5
queue[3, 1]still waiting
Take 2 off the front and add it to the answer. It was queued before anything still waiting, so no vertex closer to 0 is left behind it.
queueddone01234visited✓0✓1✓2✓34queue31frontanswer020 already visited → skip
edge2 – 0neighbour of 2
visited[0]truealready in the answer
queue[3, 1]unchanged
0 is already in the answer: this is the same edge seen from the other end. The visited flag stops the search from walking back.
queueddone01234visited✓0✓1✓2✓3✓4queue314frontanswer024 is new → mark it, queue it
edge2 – 4neighbour of 2
visited[4]trueset now, at enqueue
queue[3, 1, 4]joins the back
4 has not been seen, so mark it and put it at the back of the queue. It waits behind 3, 1, which were found earlier and are no farther from 0.
queueddone01234visited✓0✓1✓2✓3✓4queue14frontanswer023take 3 from the front
u3front of the queue
answer[0, 2, 3]3 of 5
queue[1, 4]still waiting
Take 3 off the front and add it to the answer. It was queued before anything still waiting, so no vertex closer to 0 is left behind it.
queueddone01234visited✓0✓1✓2✓3✓4queue14frontanswer0230 already visited → skip
edge3 – 0neighbour of 3
visited[0]truealready in the answer
queue[1, 4]unchanged
0 is already in the answer: this is the same edge seen from the other end. The visited flag stops the search from walking back.
queueddone01234visited✓0✓1✓2✓3✓4queue4frontanswer0231take 1 from the front
u1front of the queue
answer[0, 2, 3, 1]4 of 5
queue[4]still waiting
Take 1 off the front and add it to the answer. It was queued before anything still waiting, so no vertex closer to 0 is left behind it.
queueddone01234visited✓0✓1✓2✓3✓4queue4frontanswer02310 already visited → skip
edge1 – 0neighbour of 1
visited[0]truealready in the answer
queue[4]unchanged
0 is already in the answer: this is the same edge seen from the other end. The visited flag stops the search from walking back.
queueddone01234visited✓0✓1✓2✓3✓4queueemptyanswer02314take 4 from the front
u4front of the queue
answer[0, 2, 3, 1, 4]5 of 5
queue[]nothing else waiting
Take 4 off the front and add it to the answer. It was queued before anything still waiting, so no vertex closer to 0 is left behind it.
queueddone01234visited✓0✓1✓2✓3✓4queueemptyanswer023142 already visited → skip
edge4 – 2neighbour of 4
visited[2]truealready in the answer
queue[]unchanged
2 is already in the answer: this is the same edge seen from the other end. The visited flag stops the search from walking back.
queueddone01234visited✓0✓1✓2✓3✓4queueemptyanswer02314queue empty → return [0, 2, 3, 1, 4]
answer[0, 2, 3, 1, 4]breadth-first order
work5 vertices, 8 edge checksO(V + E)
Done. The queue is empty, so every vertex reachable from 0 has been taken out exactly once. The answer lists them nearest first: 0, then its neighbours, then theirs.
05

Common pitfalls

Marking a vertex when it leaves the queue

✗ Wrong
u = q.popleft()
visited[u] = True
for v in adj[u]:
    if not visited[v]:
        q.append(v)
✓ Right
for v in adj[u]:
    if not visited[v]:
        visited[v] = True
        q.append(v)

Between entering and leaving the queue the vertex is unmarked, so a second neighbour queues it again. In the second example vertex 3 is queued by both 1 and 2 and shows up twice in the answer.

Using a Python list with pop(0) as the queue

✗ Wrong
q = [0]
u = q.pop(0)
✓ Right
q = deque([0])
u = q.popleft()

list.pop(0) shifts every remaining element left, which is O(V) per removal and O(V²) overall. deque.popleft() is O(1).

06

Complexity

Time
O(V + E)
Space
O(V)
Each vertex is queued and taken out once; each undirected edge is checked from both ends.
07

BFS of Graph FAQ

What is the difference between BFS and DFS of a graph?

BFS uses a queue and finishes every vertex at distance k before any at distance k + 1. DFS uses a stack or recursion and follows one path as deep as it goes before backing up. Both run in O(V + E), but only a BFS graph traversal gives shortest paths in an unweighted graph.

How do you run BFS on a disconnected graph?

Loop over every vertex. Whenever one is still unvisited, mark it, queue it and run the same BFS loop from it. Each start covers one connected component, and the total work stays O(V + E). This problem promises a connected graph, so one start at 0 is enough.