cconnected.
Understand the pattern. Find your own way.Find your starting point
Your learning feed/Idea 82 of 218
Ideas this builds on
Graphs82 / 218

Strongly connected components

Picture

A↔B and B→C form two mutual-reachability groups, not one. C cannot return. Shrinking {A,B} into one dot leaves a one-way edge to {C}. The result is a DAG even when the original graph has cycles.

Predict

Why can the graph of components not contain a cycle?

Walk through the small example

A cycle between components would let every component on that cycle reach every other one in both directions. They would actually be one strongly connected component.

Mental model and complete Python solution

Kosaraju makes two DFS passes. First record vertices when their outgoing exploration finishes. Across an edge between distinct components, the source component has a later maximum finish time than the destination. Then reverse every edge and process vertices in decreasing finish order. The next unassigned component cannot escape to another unassigned component in the reversed graph, so one DFS collects exactly it. Unlike DSU, both passes respect arrow direction. The condensation DAG preserves inter-component reachability; an optimization inside a component still needs its own justified summary.

What the function promises: graph[u] lists directed neighbors with labels 0..n−1. Return components as lists of vertices and the corresponding condensation adjacency list. Component numbering is implementation-defined.

def strongly_connected(graph):
    n = len(graph)
    reverse = [[] for _ in range(n)]
    for u in range(n):
        for v in graph[u]:
            reverse[v].append(u)
    seen, finish = set(), []
    for start in range(n):
        if start in seen:
            continue
        seen.add(start)
        stack = [(start, iter(graph[start]))]
        while stack:
            u, neighbors = stack[-1]
            try:
                v = next(neighbors)
                if v not in seen:
                    seen.add(v)
                    stack.append((v, iter(graph[v])))
            except StopIteration:
                finish.append(u)
                stack.pop()

    component_of = [-1] * n
    components = []
    for start in reversed(finish):
        if component_of[start] != -1:
            continue
        label = len(components)
        component_of[start] = label
        stack, members = [start], []
        while stack:
            u = stack.pop()
            members.append(u)
            for v in reverse[u]:
                if component_of[v] == -1:
                    component_of[v] = label
                    stack.append(v)
        components.append(members)
    dag = [set() for _ in components]
    for u in range(n):
        for v in graph[u]:
            a, b = component_of[u], component_of[v]
            if a != b:
                dag[a].add(b)
    return components, dag

groups, dag = strongly_connected([[1], [0, 2], []])
assert {frozenset(group) for group in groups} == {frozenset({0,1}), frozenset({2})}
assert sum(map(len, dag)) == 1
assert strongly_connected([]) == ([], [])

Work and storage: O(V+E) expected time including set-based edge deduplication, and O(V+E) space. Explicit stacks avoid recursion-depth failure on long chains.

When to recognize it: Mutual reachability forms groups. Shrinking every group turns the remaining connections into a DAG.

Check before applying it: Union-find ignores arrow direction. Also, compressing a cycle does not automatically solve an optimization problem inside it.

Change one rule

Each node gives a reward at most once, revisits are allowed, and all rewards are nonnegative. Can a component’s reward be its sum?

See what changes

Yes: inside a strongly connected component, a walk can visit all members and then reach a chosen exit, revisiting nodes when needed. Sum its nonnegative rewards and use DAG DP between components. If revisits are forbidden or rewards/cost rules change, that sum may no longer describe a feasible internal walk.

Connect

Course Schedule II: The condensed graph can be evaluated in topological order.

Parallel Courses III: Use DAG DP only after defining what each component can safely summarize.