Connected Components Using BFS in DSA Python - Time & Space Complexity
We want to understand how long it takes to find all connected parts in a graph using BFS.
How does the time needed grow when the graph gets bigger?
Analyze the time complexity of the following code snippet.
def connected_components(graph):
visited = set()
count = 0
for node in graph:
if node not in visited:
queue = [node]
visited.add(node)
while queue:
current = queue.pop(0)
for neighbor in graph[current]:
if neighbor not in visited:
visited.add(neighbor)
queue.append(neighbor)
count += 1
return count
This code counts how many connected groups are in an undirected graph using BFS.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Visiting each node and its neighbors once using BFS.
- How many times: Each node and edge is checked once during the BFS traversals.
As the graph grows, the time grows with the number of nodes and edges.
| Input Size (n nodes, m edges) | Approx. Operations |
|---|---|
| 10 nodes, 15 edges | About 25 checks (nodes + edges) |
| 100 nodes, 200 edges | About 300 checks |
| 1000 nodes, 5000 edges | About 6000 checks |
Pattern observation: The work grows roughly with the sum of nodes and edges.
Time Complexity: O(n + m)
This means the time grows linearly with the number of nodes plus edges in the graph.
[X] Wrong: "The BFS runs in O(n²) because of the nested loops."
[OK] Correct: Each edge and node is visited only once, so the total work is proportional to nodes plus edges, not their product.
Understanding BFS time complexity helps you explain graph traversal efficiency clearly, a key skill in many coding interviews.
"What if the graph was represented as an adjacency matrix instead of adjacency lists? How would the time complexity change?"
Practice
Solution
Step 1: Understand BFS in graph context
BFS explores all nodes reachable from a given start node by visiting neighbors level by level.Step 2: Connect BFS to connected components
Using BFS from an unvisited node finds all nodes in that node's connected component.Final Answer:
All nodes reachable from a starting node forming one connected component -> Option AQuick Check:
BFS finds connected components by exploring reachable nodes [OK]
- Confusing BFS with shortest path only
- Thinking BFS counts edges
- Mixing BFS with node degree calculation
Solution
Step 1: Identify BFS queue requirements
BFS requires a queue data structure to process nodes in FIFO order.Step 2: Choose correct Python queue type
collections.deque() is efficient for queue operations (append and popleft).Final Answer:
queue = collections.deque() -> Option DQuick Check:
Use deque for BFS queue in Python [OK]
- Using list which is inefficient for popping front
- Using set which is unordered
- Using dict which is key-value store
graph = {0: [1], 1: [0, 2], 2: [1], 3: []}
visited = set()
components = []
for node in graph:
if node not in visited:
queue = collections.deque([node])
comp = []
while queue:
current = queue.popleft()
if current not in visited:
visited.add(current)
comp.append(current)
queue.extend(n for n in graph[current] if n not in visited)
components.append(sorted(comp))
print(components)Solution
Step 1: Analyze graph structure
Nodes 0,1,2 are connected; node 3 is isolated.Step 2: Trace BFS for each component
Starting at 0, BFS visits 0,1,2; then 3 alone forms second component.Final Answer:
[[0, 1, 2], [3]] -> Option CQuick Check:
Connected components = [[0,1,2],[3]] [OK]
- Not sorting component lists
- Mixing isolated node with others
- Forgetting to mark visited nodes
def bfs_components(graph):
visited = set()
components = []
for node in graph:
if node not in visited:
queue = [node]
comp = []
while queue:
current = queue.pop()
if current not in visited:
visited.add(current)
comp.append(current)
for neighbor in graph[current]:
if neighbor not in visited:
queue.append(neighbor)
components.append(comp)
return componentsSolution
Step 1: Check queue operations
Using list pop() removes last element, making it DFS, not BFS.Step 2: Understand BFS queue behavior
BFS requires FIFO, so pop(0) or deque.popleft() is needed.Final Answer:
Using list pop() removes from end, not front, breaking BFS order -> Option AQuick Check:
pop() removes last, use popleft() for BFS [OK]
- Using pop() instead of popleft()
- Not marking visited before enqueue
- Appending neighbors without check
graph = {0: [1, 2], 1: [0, 3], 2: [0], 3: [1], 4: [5], 5: [4], 6: []}Write a BFS-based approach to find the number of connected components and return the size of the largest component.
Solution
Step 1: Identify connected components
Components are: {0,1,2,3}, {4,5}, {6} totaling 3 components.Step 2: Calculate sizes
Sizes are 4, 2, and 1 respectively; largest is 4 nodes.Step 3: Re-check sizes carefully
Node 6 is isolated (size 1), so largest component size is 4.Final Answer:
Number of components = 3, Largest size = 4 -> Option BQuick Check:
3 components, largest has 4 nodes [OK]
- Counting isolated nodes incorrectly
- Mixing component sizes
- Forgetting to update largest size
Ready to prove it?
Pass the test and this pattern moves unknown
