Zero To DSAZero To DSA
Privacy Policy·Support on Ko‑fi
Learning Path
Big O & ComplexityArrays & StringsHashMaps & SetsLinked ListsStacks & QueuesRecursion & BacktrackingSearching & SortingGreedy & IntervalsTrees & TriesGraphsDynamic Programming

Graphs

advanced5 problems· Prerequisites: Big O & Complexity, Arrays & Strings, Stacks & Queues, Trees & Tries
Learn
Practice
Exam

Graph Representation

A graph consists of vertices (nodes) and edges connecting them. Graphs can be directed or undirected, weighted or unweighted.

Representations:

ApproachSpaceCheck edgeIterate neighbors
Adjacency MatrixO(V²)O(1)O(V)
Adjacency ListO(V + E)O(degree)O(degree)
Edge ListO(E)O(E)O(E)

Adjacency list is the most common choice for interviews.

Building a graph (adjacency list)C#
// Graph as adjacency list
var graph = new Dictionary<int, List<int>>();

// Build from edges
void BuildGraph(int[][] edges) {
    foreach (var e in edges) {
        int u = e[0], v = e[1];
        if (!graph.ContainsKey(u)) graph[u] = new List<int>();
        if (!graph.ContainsKey(v)) graph[v] = new List<int>();
        graph[u].Add(v);
        graph[v].Add(u); // undirected - add both directions
    }
}

// Graph represented as List<int>[] (if vertices are 0..n-1)
int n = 6;
var graph2 = new List<int>[n];
for (int i = 0; i < n; i++) graph2[i] = new List<int>();

// Each edge: graph2[u].Add(v); graph2[v].Add(u);

// Weighted graph
var weighted = new Dictionary<int, List<(int neighbor, int weight)>>();
weighted[u].Add((v, 5));

BFS vs DFS

BFS (Queue)DFS (Stack/Recursion)
OrderLevel by levelDeep first
Shortest pathYes (unweighted)No
SpaceO(width)O(depth)
Use caseShortest path, level orderTopological, connectivity, cycles

Key difference: BFS uses a queue and explores neighbors level by level. DFS uses a stack (or recursion) and goes deep before backtracking.

Problem

Explore all nodes of this graph starting from node A, visiting each node exactly once.

Graph TraversalStep 1 / 12
012345
Initialize queue with 0
Speed:
Unvisited Next in line Current Visited
BFS and DFS templatesC#
// BFS - shortest path in unweighted graph
int Bfs(Dictionary<int, List<int>> graph, int start, int target) {
    var q = new Queue<int>();
    var visited = new HashSet<int>();
    q.Enqueue(start);
    visited.Add(start);
    int distance = 0;

    while (q.Count > 0) {
        int size = q.Count;
        for (int i = 0; i < size; i++) {
            int node = q.Dequeue();
            if (node == target) return distance;

            foreach (var neighbor in graph.GetValueOrDefault(node, [])) {
                if (visited.Add(neighbor))
                    q.Enqueue(neighbor);
            }
        }
        distance++;
    }
    return -1; // not reachable
}

// DFS - recursive (for connectivity, cycle detection)
bool Dfs(Dictionary<int, List<int>> graph, int node, HashSet<int> visited) {
    if (visited.Contains(node)) return false;
    visited.Add(node);

    foreach (var neighbor in graph.GetValueOrDefault(node, []))
        Dfs(graph, neighbor, visited);

    return true;
}

Topological Sort

A topological order of a directed acyclic graph (DAG) is an ordering where every edge goes from earlier to later nodes.

Applications: Course prerequisites, build dependencies, task scheduling.

Two algorithms:

  1. Kahn's algorithm (BFS) - track in-degrees, process nodes with 0 in-degree
  2. DFS-based - post-order traversal, reverse the result
Kahn's algorithm (BFS)C#
int[] TopologicalSort(int n, int[][] edges) {
    var graph = new List<int>[n];
    var inDegree = new int[n];
    for (int i = 0; i < n; i++) graph[i] = new List<int>();

    foreach (var e in edges) {
        graph[e[0]].Add(e[1]);
        inDegree[e[1]]++;
    }

    var q = new Queue<int>();
    for (int i = 0; i < n; i++)
        if (inDegree[i] == 0) q.Enqueue(i);

    var result = new List<int>();
    while (q.Count > 0) {
        int node = q.Dequeue();
        result.Add(node);

        foreach (var neighbor in graph[node]) {
            if (--inDegree[neighbor] == 0)
                q.Enqueue(neighbor);
        }
    }

    return result.Count == n ? result.ToArray() : []; // [] if cycle
}

When to Model as a Graph

A graph is the right abstraction when:

  • You have entities (nodes) and relationships (edges) between them
  • The relationships are not hierarchical (use a tree if they are)
  • You need to find paths, connectivity, or shortest distances

Graph vs other structures:

StructureUse when
TreeStrict hierarchy (one parent per node)
GraphArbitrary connections (many-to-many)
Adjacency listMost interview problems (space-efficient)
Adjacency matrixDense graph, need O(1) edge check

BFS vs DFS decision guide:

SignalChoice
"Shortest path in unweighted graph"BFS (queue)
"Does a path exist?"Either
"Topological order"BFS (Kahn's) or DFS
"Detect cycle"DFS with recursion stack
"Connected components"Either (DFS is simpler)
"All paths from A to B"DFS (backtracking)
"Level order / layers"BFS
"Maze shortest path"BFS
"Maze find any exit"DFS (simpler)

When NOT to use a graph:

  • Only one entity type with no relationships (just use a list)
  • Relationships are simple and linear (use an array or tree)
  • Problem can be solved with a hash map and iteration

Common Mistakes / Gotchas

Forgetting the visited set (infinite loop) Without tracking visited nodes, BFS/DFS will revisit nodes and loop forever in a cyclic graph. Always initialize a visited set before traversal and check it before enqueuing/exploring.

Confusing directed and undirected graphs In an undirected graph, add both directions: graph[u].Add(v) AND graph[v].Add(u). In a directed graph, only add one direction. Missing this causes wrong answers.

"BFS always finds the shortest path" Only for unweighted graphs. In weighted graphs, BFS does NOT find the shortest path - use Dijkstra's algorithm instead.

DFS recursion depth in large graphs A DFS on a graph with 100,000 nodes might recurse 100,000 levels deep. Stack overflow! Use explicit stack (iterative DFS) for large graphs.

Confusing BFS and DFS approaches BFS = queue (level by level). DFS = stack/recursion (depth first). Using the wrong one wastes time or gives wrong answers. For shortest path in an unweighted graph, always choose BFS.

Not handling disconnected components Most graph problems assume a connected graph. If the graph has multiple components, make sure your traversal covers ALL nodes by looping over all vertices and starting a new traversal for any unvisited node.

Common Interview Patterns

  1. Number of islands / connected components - DFS or BFS on grid
  2. Course schedule / prerequisite order - topological sort
  3. Clone graph - BFS/DFS with hash map to track cloned nodes
  4. Word ladder - BFS shortest path on implicit graph
  5. Detect cycle - DFS with recursion stack tracking (3-coloring: unvisited, in-stack, done)
  6. Dijkstra's shortest path - PriorityQueue for weighted graphs