Knowledge Guide
HomeDSAGraphs

Types of Graph

A graph's type is not a label you memorize — it is a set of structural constraints on its edge relation (direction, weight, cycle-freedom, reachability) that determines which traversal invariants hold and therefore which algorithms are even valid to run on it. Classifying a graph correctly, before picking an algorithm, is what separates a working solution from an infinite loop or a wrong answer.

Recognize the pattern

Brute force → optimal (determining the type)

Brute force cycle check: for every pair of vertices, enumerate all simple paths between them and see if any path returns to its start. This is exponential — a complete graph on V vertices has on the order of (V−2)! simple paths between any two fixed vertices, not V!, but it is still combinatorially explosive and unusable beyond tiny graphs.

Optimal: a single DFS with 3-color marking (white/gray/black) detects a cycle in one pass: a back-edge to a gray (currently-on-stack) node means a cycle exists. Connectivity and strong connectivity have the same brute-force-vs-optimal gap: brute force runs BFS/DFS from every vertex (O(V·(V+E))); the optimal approach for strong connectivity is Kosaraju's or Tarjan's algorithm, which finds all strongly connected components using a constant number of linear passes — a second DFS on the transpose graph (Kosaraju) or a single DFS maintaining a low-link array (Tarjan) — instead of restarting a traversal from every vertex.

Complexity, derived

DFS/BFS visits each vertex once and relaxes each edge once (undirected: twice, once per endpoint's adjacency list) → O(V+E) time, O(V) space for the visited/color array and recursion stack. Naive per-vertex traversal for connectivity repeats this V times → O(V·(V+E)). Kosaraju's SCC algorithm runs DFS twice (once for finish order, once on the transpose) — still two linear passes → O(V+E) total, O(V) extra space for the finish-order stack and component labels. Union-Find based cycle detection on an undirected graph processes E edges with near-constant find/union → O(E·α(V)), essentially linear.

Worked example: classifying with one DFS pass

Directed graph: edges A→B, B→C, C→A, C→D. DFS from A, colors start all WHITE.

StepVisitColor set to GRAYEdge examinedResult
1AAA→BB is WHITE, recurse
2BBB→CC is WHITE, recurse
3CCC→AA is GRAY (on stack) → back edge → cycle found
4CC→DD is WHITE, recurse, finishes, backtrack

Because a back edge to a GRAY node was found, this directed graph is cyclic — it is not a DAG, so no topological sort exists until the A→B→C→A cycle is broken.

Implementation

3-color DFS cycle detection (directed graph):

enum Color { WHITE, GRAY, BLACK }

boolean hasCycle(int v, Map<Integer, List<Integer>> adj, Color[] color) {
    color[v] = Color.GRAY;
    for (int u : adj.getOrDefault(v, List.of())) {
        if (color[u] == Color.GRAY) return true;        // back edge -> cycle
        if (color[u] == Color.WHITE && hasCycle(u, adj, color)) return true;
    }
    color[v] = Color.BLACK;
    return false;
}

Undirected cycle detection (visited + parent, self-loop aware):

boolean hasCycleUndirected(int v, int parent, Map<Integer, List<Integer>> adj, boolean[] visited) {
    visited[v] = true;
    for (int u : adj.getOrDefault(v, List.of())) {
        if (u == v) return true;                 // self-loop: trivial cycle of length 1
        if (!visited[u]) {
            if (hasCycleUndirected(u, v, adj, visited)) return true;
        } else if (u != parent) {
            return true;                          // back edge to a distinct, already-visited vertex
        }
    }
    return false;
}

Union-Find cycle detection (undirected):

int[] parent;
int find(int x) { return parent[x] == x ? x : (parent[x] = find(parent[x])); }
boolean union(int a, int b) {
    int ra = find(a), rb = find(b);
    if (ra == rb) return false;   // already in same set -> edge (a, b) would close a cycle
    parent[ra] = rb;
    return true;
}
// for each edge (a, b): if a == b it is a self-loop cycle; else if (!union(a, b)) a cycle is found

Kosaraju's SCC algorithm (two linear DFS passes):

void kosaraju(Map<Integer, List<Integer>> adj, int n) {
    boolean[] visited = new boolean[n];
    Deque<Integer> finishOrder = new ArrayDeque<>();
    for (int v = 0; v < n; v++) if (!visited[v]) fillOrder(v, adj, visited, finishOrder);

    Map<Integer, List<Integer>> transposed = transpose(adj, n);
    Arrays.fill(visited, false);
    List<List<Integer>> sccs = new ArrayList<>();
    while (!finishOrder.isEmpty()) {
        int v = finishOrder.pop();
        if (!visited[v]) {
            List<Integer> component = new ArrayList<>();
            collect(v, transposed, visited, component);   // fillOrder / collect are standard DFS helpers
            sccs.add(component);
        }
    }
}

Pitfalls

When to use / when not, and trade-offs

Classify the graph before choosing storage and algorithm: a DAG signals topological sort / DP-on-DAG opportunities (longest path, task scheduling); a weighted graph signals Dijkstra/Bellman-Ford/Floyd-Warshall instead of plain BFS; an unweighted graph makes BFS sufficient and Dijkstra wasteful overkill. Representation trade-off: adjacency list (O(V+E) space) suits sparse graphs and is the default for most classifications above; adjacency matrix (O(V²) space) suits dense graphs or when O(1) edge-existence checks matter, e.g. checking if a specific edge exists for strongly-connected-graph queries. Don't reach for Tarjan/Kosaraju (O(V+E)) if you only need weak connectivity on an undirected graph — a single BFS/DFS already answers that in the same time with less code. Similarly, prefer Union-Find over a full DFS-based cycle check when edges arrive incrementally (e.g. Kruskal's MST) — it avoids re-traversing the graph on every insertion.

Takeaways

Recall: Why does 2-color (visited/unvisited) DFS cycle detection give false positives on a directed graph, and what does the 3rd color fix?


Synthesized from CLRS (Introduction to Algorithms) graph traversal chapters and standard competitive-programming graph taxonomy references.

🤖 Don't fully get this? Learn it with Claude

Stuck on Types of Graph? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🎨 Explain it visually

Build the mental picture, not memorization.

I just read a lesson on **Types of Graph** (DSA) and want to truly understand it. Explain Types of Graph from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🤔 Walk me through it (interactive)

Socratic — adapts to where you're stuck.

Teach me **Types of Graph** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧪 Quiz me & fix my gaps

Active recall exposes what you missed.

Quiz me on **Types of Graph** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧠 Make it stick

Intuition + hook + flashcards for long-term memory.

Help me remember **Types of Graph** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes