CommonLounge Archive

Breadth-first search and Depth-first search

December 21, 2016

Video for concept and walkthrough

First, let’s watch a slow-paced easy-to-follow walkthrough of the breadth-first search and depth-first search algorithms.

Breadth-first search

Breath-first search explores a graph from a source vertex in a way such that all nodes at a distance of 1 are encountered first, then all nodes at a distance of 2, then all nodes at a distance of 3 and so on.

Pseudocode

define breadth_first_search(source):
    # initialization 
    visited[node] = False for all nodes in graph
    queue = empty queue 
    # start from the source
    visited[source] = True
    queue.push(source) # push to the back of the queue 
    # keep searching
    while queue is not empty: 
        node = queue.pop() # pop node from front of queue
        for each neighbor of node:
            if not visited[neighbor]:
                queue.push(neighbor)  # push to the back of the queue

Since we use a queue for nodes, the nodes that gets discovered first, also gets expanded first. That is, breadth-first search. The video above includes a detailed example and walkthrough.

Code Implementation

The following is the implementation of bfs in cpp.

#include <bits/stdc++.h>
using namespace std;
const int N = 100;
bool vis[N] = {};
vector<int>adj_list[N];
int main() 
{
  // Number of nodes and edges
  int n = 6, m = 5;
  /*
    We will be adding the 
    following 5 edges.
    (0,1), (0,2), (0,3),
    (2,4), (4,1)
  */
  pair<int, int> edges[] = {{0, 1}, {0, 2}, {0, 3}, {2, 4}, {4, 1}};
  for (int i = 0; i < m; i++)
  {
    int x = edges[i].first;
    int y = edges[i].second;
    // As graph is undirected
    adj_list[x].push_back(y);
    adj_list[y].push_back(x);
  }
  queue<int>q;
  q.push(0);
  vis[0] = 1;
  while (!q.empty())
  {
    int node = q.front();
    q.pop();
    cout << "Visited node: " << node << endl;
    for (auto i:adj_list[node])
    {
      if (!vis[i])
      {
        q.push(i);
        vis[i] = 1;
      }
    }
  }
  return 0;
}

The following is the implementation of BFS in python:

from collections import defaultdict, deque
N = 100
graph = defaultdict(list)
# adding the following edges
edges = [(0, 1), (0, 2), (0, 3), (2, 4), (4, 1)]
for i in range (0, len(edges)):
    x = edges[i][0]
    y = edges[i][1]
    graph[x].append(y)
    graph[y].append(x)
visited, queue = set(), deque([0])
visited.add(0)
while queue: 
    vertex = queue.popleft()
    print("Visited " + str(vertex))
    for neighbour in graph[vertex]: 
        if neighbour not in visited: 
            visited.add(neighbour) 
            queue.append(neighbour)

Depth-first search

Depth-first search explores a graph from a source vertex. It explores all the nodes it can from the 1st neighbor before moving on to the next neighbor.

Recursive Approach

It is easier to implement depth-first search in a recursive way. The pseudo code is as follows.

# initialization 
visited[node] = False for all nodes in graph
define depth_first_search(node):
    visited[node] = True
    for each neighbor of node:
        if not visited[neighbor]:
            depth_first_search(neighbor)

We expand a node as soon as we discover it. By the time we explore the next neighbor, all nodes that can be explored from the current neighbor have already been explored. That is, depth-first search. The video above includes a detailed example and walkthrough.

Code Implementation

The following is the recursive implementation of dfs in cpp.

#include <bits/stdc++.h>
using namespace std;
const int N = 100;
bool vis[N] = {};
vector<int>adj_list[N];
void dfs(int node)
{
  vis[node] = 1;
  cout << "Reached node: " << node << endl;
  for (auto i: adj_list[node])
  {
    if (!vis[i])
      dfs(i);
  }
}
int main() 
{
  // Number of nodes and edges
  int n = 6, m = 5;
  /*
    We will be adding the 
    following 5 edges.
    (0,1), (0,2), (0,3),
    (2,4), (4,1)
  */
  pair<int, int> edges[] = {{0, 1}, {0, 2}, {0, 3}, {2, 4}, {4, 1}};
  for (int i = 0; i < m; i++)
  {
    int x = edges[i].first;
    int y = edges[i].second;
    // As graph is undirected
    adj_list[x].push_back(y);
    adj_list[y].push_back(x);
  }
  dfs(0);
  return 0;
}

The following is the code implementation of dfs in python:

from collections import defaultdict
graph = defaultdict(list)
visited = []
def dfs(node):
    print("Visited: " + str(node))
    visited.append(node)
    for n in graph[node]:
        if n not in visited:
            dfs(n)
# adding the following edges
edges = [(0, 1), (0, 2), (0, 3), (2, 4), (4, 1)]
for i in range (0, len(edges)):
    x = edges[i][0]
    y = edges[i][1]
    graph[x].append(y)
    graph[y].append(x)
dfs(0)

Iterative Approach

Depth-first search can have a iterative implementation as well, but we have to be more careful about when we mark a node as visited and when we check whether a node has been visited. The following is the pseudo code for the iterative approach.

define depth_first_search(source):
    # initialization 
    visited[node] = False for all nodes in graph
    stack = empty stack
    # start from the source
    stack.push(source)  # push to the top of the stack 
    # keep searching
    while stack is not empty: 
        node = stack.pop() # pop node from top of stack
        if not visited[node]:
            visited[node] = True
            for each neighbor of node:
                stack.push(neighbor) # push to the top of the stack

Run-time analysis

For a graph with $n$ nodes and $m$ edges, both depth-first search and breadth-first search have a run-time of $O(m)$. In both the algorithms, we explore each node exactly once, and hence we look at each edge exactly once.

Further reading

See the replies to this discussion for bonus videos. The tutorials on IARCS Online Study material for breadth-first search and depth-first search are very comprehensive. They include a text tutorials (if you prefer reading over videos), pseudocode and also longer videos (20 minute each).

Next steps

In the upcoming tutorials, we’ll practice implementing breadth-first search and depth-first search.


© 2016-2022. All rights reserved.