Tutorials Logic, IN info@tutorialslogic.com

Graph Data Structure: Adjacency List, BFS and DFS

Graph Representation Choices

A graph models entities as vertices and relationships as edges. Choose an adjacency list for most sparse graphs and an adjacency matrix when constant-time edge lookup justifies the extra space.

Use BFS for level-order exploration and shortest paths in unweighted graphs; use DFS for deep traversal, components, and cycle-oriented reasoning. Mark a vertex when it is scheduled so cycles do not create duplicate work.

An adjacency list stores only existing edges and is efficient for sparse graphs. An adjacency matrix uses a grid and is useful when edge lookup must be constant-time or the graph is dense.

  • Use adjacency lists for most interview problems.
  • Use matrices for dense graphs or quick edge existence checks.
  • Include weights in pairs or objects when edges have cost.

BFS Versus DFS

BFS explores level by level and is useful for shortest path in an unweighted graph. DFS explores deeply first and is useful for connected components, cycle detection, and topological-style reasoning.

  • Use BFS for minimum edge distance.
  • Use DFS for exhaustive exploration.
  • Always mark visited nodes.

Graph Problem Patterns

Many advanced problems are graph problems with different names: dependency ordering, islands in a grid, friend circles, route planning, deadlock detection, and package installation order.

  • Translate the problem into nodes and edges.
  • Decide whether direction matters.
  • Choose traversal based on the question being asked.

Handle Disconnected Graphs and Visited Ownership

Starting BFS or DFS from one vertex visits only its reachable component. To traverse a general graph, iterate over every vertex and launch a traversal whenever that vertex is still unvisited. This outer loop changes the contract from component traversal to complete graph traversal.

Mark visited state when work is scheduled, not after it is processed. In BFS, marking on enqueue prevents several parents from adding the same vertex. In iterative DFS, the same rule prevents unnecessary stack growth. Decide whether visited state belongs to one operation or persists across operations; stale shared state can make later traversals skip valid vertices.

  • Include isolated vertices in the representation and traversal test.
  • For undirected graphs, add both adjacency directions exactly once.
  • Test a self-loop, a cycle, and two disconnected components.

Adjacency List and BFS

Adjacency List and BFS
#include <iostream>
#include <queue>
#include <vector>
using namespace std;

int main() {
    vector<vector<int>> graph = {
        {1, 2},
        {0, 3},
        {0},
        {1}
    };

    vector<bool> visited(graph.size(), false);
    queue<int> q;
    q.push(0);
    visited[0] = true;

    while (!q.empty()) {
        int node = q.front();
        q.pop();
        cout << node << ' ';

        for (int next : graph[node]) {
            if (!visited[next]) {
                visited[next] = true;
                q.push(next);
            }
        }
    }
}

DFS Traversal

DFS Traversal
#include <iostream>
#include <vector>
using namespace std;

void dfs(int node, vector<vector<int>>& graph, vector<bool>& visited) {
    visited[node] = true;
    cout << node << ' ';

    for (int next : graph[node]) {
        if (!visited[next]) {
            dfs(next, graph, visited);
        }
    }
}

Connected Components Count

Connected Components Count
#include <vector>
using namespace std;

void dfs(int node, vector<vector<int>>& graph, vector<bool>& seen) {
    seen[node] = true;
    for (int next : graph[node]) {
        if (!seen[next]) dfs(next, graph, seen);
    }
}

int components(vector<vector<int>>& graph) {
    vector<bool> seen(graph.size(), false);
    int count = 0;
    for (int i = 0; i < (int)graph.size(); i++) {
        if (!seen[i]) {
            count++;
            dfs(i, graph, seen);
        }
    }
    return count;
}

Weighted Adjacency List

Weighted Adjacency List
#include <vector>
#include <utility>
using namespace std;

int main() {
    vector<vector<pair<int, int>>> graph(3);
    graph[0].push_back({1, 7});
    graph[0].push_back({2, 4});
    graph[1].push_back({2, 2});
}
Before you move on

Graph Representation and Traversal Check

2 checks
  • A graph stores relationships between vertices using edges.
  • Beginners should understand directed versus undirected graphs, weighted versus unweighted graphs, adjacency lists, adjacency matrices, BFS, and DFS.

Graph Representation Boundary

  • Directedness mismatch

    Adding one adjacency entry creates a directed edge; an undirected edge requires the reverse entry too. State the graph type and test isolated and disconnected vertices.

Data Structure Questions Learners Ask

BFS explores level by level with a queue; DFS follows one path at a time with recursion or a stack.

Use a list for sparse graphs because it stores only existing edges.

Without a visited set, cycles can cause repeated work or an infinite traversal.

Browse Free Tutorials

Explore 500+ free tutorials across 20+ languages and frameworks.