• JUPYTER
  • FAQ
  • View as Code
  • Python 3 Kernel
  • View on GitHub
  • Execute on Binder
  • Download Notebook
  1. interactive-coding-challenges
  2. graphs_trees
  3. graph_bfs

This notebook was prepared by Donne Martin. Source and license info is on GitHub.

Solution Notebook¶

Problem: Implement breadth-first search on a graph.¶

  • Constraints
  • Test Cases
  • Algorithm
  • Code
  • Unit Test

Constraints¶

  • Is the graph directed?
    • Yes
  • Can we assume we already have Graph and Node classes?
    • Yes
  • Can we assume this is a connected graph?
    • Yes
  • Can we assume the inputs are valid?
    • Yes
  • Can we assume this fits memory?
    • Yes

Test Cases¶

Input:

  • add_edge(source, destination, weight)
graph.add_edge(0, 1, 5)
graph.add_edge(0, 4, 3)
graph.add_edge(0, 5, 2)
graph.add_edge(1, 3, 5)
graph.add_edge(1, 4, 4)
graph.add_edge(2, 1, 6)
graph.add_edge(3, 2, 7)
graph.add_edge(3, 4, 8)

Result:

  • Order of nodes visited: [0, 1, 4, 5, 3, 2]

Algorithm¶

We generally use breadth-first search to determine the shortest path.

  • Add the current node to the queue and mark it as visited
  • While the queue is not empty
    • Dequeue a node and visit it
    • Iterate through each adjacent node
      • If the node has not been visited, add it to the queue and mark it as visited

Complexity:

  • Time: O(V + E), where V = number of vertices and E = number of edges
  • Space: O(V)

Note on space complexity from Wikipedia:

  • When the number of vertices in the graph is known ahead of time, and additional data structures are used to determine which vertices have already been added to the queue, the space complexity can be expressed as O(V)
  • If the graph is represented by an adjacency list it occupies O(V + E) space in memory
  • If the graph is represented by an adjacency matrix representation, it occupies O(V^2)

Code¶

In [1]:
%run ../graph/graph.py
In [2]:
from collections import deque


class GraphBfs(Graph):

    def bfs(self, root, visit_func):
        if root is None:
            return
        queue = deque()
        queue.append(root)
        root.visit_state = State.visited
        while queue:
            node = queue.popleft()
            visit_func(node)
            for adjacent_node in node.adj_nodes.values():
                if adjacent_node.visit_state == State.unvisited:
                    queue.append(adjacent_node)
                    adjacent_node.visit_state = State.visited

Unit Test¶

In [3]:
%run ../utils/results.py
In [4]:
%%writefile test_bfs.py
import unittest


class TestBfs(unittest.TestCase):

    def __init__(self, *args, **kwargs):
        super(TestBfs, self).__init__()
        self.results = Results()

    def test_bfs(self):
        nodes = []
        graph = GraphBfs()
        for id in range(0, 6):
            nodes.append(graph.add_node(id))
        graph.add_edge(0, 1, 5)
        graph.add_edge(0, 4, 3)
        graph.add_edge(0, 5, 2)
        graph.add_edge(1, 3, 5)
        graph.add_edge(1, 4, 4)
        graph.add_edge(2, 1, 6)
        graph.add_edge(3, 2, 7)
        graph.add_edge(3, 4, 8)
        graph.bfs(nodes[0], self.results.add_result)
        self.assertEqual(str(self.results), "[0, 1, 4, 5, 3, 2]")

        print('Success: test_bfs')


def main():
    test = TestBfs()
    test.test_bfs()


if __name__ == '__main__':
    main()
Overwriting test_bfs.py
In [5]:
%run -i test_bfs.py
Success: test_bfs

This website does not host notebooks, it only renders notebooks available on other websites.

Delivered by Fastly, Rendered by OVHcloud

nbviewer GitHub repository.

nbviewer version: 8b013f7

nbconvert version: 7.2.3

Rendered (Wed, 02 Jul 2025 13:06:12 UTC)