DSA DSA In Depth
0%
lesson bipartite2-coloringodd-cycle

Bipartite Graphs: Odd-Cycle Characterization & 2-Coloring Verification

7 min read Module 5.1: Graph Representations, Traversals, Connectivity & Topological Order Updated Oct 23, 2026
📄 Cheat Sheet
Bipartite Graph Verification & Two-Coloring via BFS/DFS
Techdose Bipartite Graph Verification & Two-Coloring via BFS/DFS Watch on YouTube ↗

Walks through BFS level parity and the proof that odd cycles are the exact obstruction to bipartiteness.

0:00 / --:--

König's Odd-Cycle Theorem & BFS Level Parity

Why is the absence of odd cycles both necessary and sufficient for a graph to be 2-colorable? Consider running BFS from an arbitrary root $r$ in a connected component and assigning each vertex $u$ to partition $L$ if its shortest-path distance $d(r, u)$ is even, or $R$ if $d(r, u)$ is odd.

Formal Theorem — Odd-Cycle Characterization of Bipartite Graphs

Theorem (König, 1916): An undirected graph $G = (V, E)$ is bipartite if and only if it contains no cycle of odd length.

Proof Sketch: ($Rightarrow$) Along any cycle $v_0, v_1, \dots, v_{k-1}, v_0$ in a 2-colored graph, colors must alternate $c(v_i) = i \bmod 2$. Since $v_k = v_0$, $k$ must be even.
($Leftarrow$) Suppose $G$ has no odd cycles. In each connected component, pick root $r$ and set $\text{color}(u) = d(r, u) \bmod 2$. If any edge ${u, v} \in E$ had $\text{color}(u) == \text{color}(v)$, let $w = \text{LCA}(u, v)$ in the BFS tree. Since $d(r, u) \equiv d(r, v) \pmod 2$, the paths $w \leadsto u$ and $w \leadsto v$ have the same parity, so the cycle $w \leadsto u - v \leadsto w$ has length $(d(r, u) - d(r, w)) + (d(r, v) - d(r, w)) + 1$, which is odd—a contradiction.

Constructing an Explicit Odd-Cycle Certificate

In production verification systems (such as register interference graphs or VLSI layer assignment), returning a boolean false is insufficient; engineers need the exact odd cycle causing the conflict. When BFS/DFS discovers an edge ${u, v}$ with $\text{color}[u] == \text{color}[v]$:

  1. Trace parent pointers $\text{parent}[u]$ and $\text{parent}[v]$ upward until they meet at their lowest common ancestor $w$ in the spanning tree.
  2. Concatenate the path $w \leadsto u$, the conflict edge $(u, v)$, and the reversed path $v \leadsto w$ to output a simple odd cycle in $O(V)$ time.
Bipartite (Even Cycle C₄): Valid 2-Coloring Non-Bipartite (Odd Cycle C₅): Same-Color Edge L(0) R(1) R(1) L(0) Conflict: d=2 — d=2 d=0 d=1 d=1 d=2 d=2
flowchart LR Start["For each uncolored vertex r in 0..V-1"] --> Seed["color[r] = 0; push r to Queue"] Seed --> Pop["Pop u; inspect each neighbor v"] Pop --> Check{"color[v] == -1?"} Check -- "Yes" --> Assign["color[v] = color[u] ^ 1; push v"] Check -- "No" --> Conflict{"color[v] == color[u]?"} Conflict -- "Yes" --> Odd["Not Bipartite: Odd Cycle Found"] Conflict -- "No" --> Next["Valid Cross-Partition Edge"]
0-1 BFS Deque, DFS Edge Classification & Tarjan tin/low Bridges & SCCs
O(V + E) Single-Pass Traversal

Step through 0-1 BFS deque relaxation (push_front for w=0, push_back for w=1), Tarjan’s undirected bridge/articulation criterion (low[v] > tin[u] / low[v] ≥ tin[u]), and Tarjan’s single-pass SCC condensation.

Step 1 / 9
Custom Input:
Timeline

Invariant & State

DFS reaches sink v5 (tin=6, low=6). Since low[v5] == tin[v5], pop v5 as SCC #1 = {v5} in reverse topological order.
back cross-SCC back v0 1/1 v1 2/1 v2 3/1 v3 4/4 v4 5/4 v5 6/6 ROOT
Active Tarjan Stack
[v0, v1, v2, v3, v4]
Extracted SCCs
SCC #1: {v5}
Root Invariant
low[u] == tin[u]
[Step 1] DFS reaches sink v5 (tin=6, low=6). Since low[v5] == tin[v5], pop v5 as SCC #1 = {v5} in reverse topological order.
Hardware Reality — 2-Bit State Packing & Branchless XOR Coloring

Storing vertex colors in a std::vector<int8_t> initialized to -1 fits $64$ vertex states into a single 64-byte L1 cache line. Assigning the opposite color uses a single branchless bitwise XOR instruction: color[v] = color[u] ^ 1 (flipping $0 \leftrightarrow 1$).

Complexity Matrix

Task General Graph Complexity Bipartite Graph Complexity Algorithm Used
k-Coloring ($k = 2$ vs $k \ge 3$) NP-Complete (k ≥ 3) O(V + E) BFS / DFS 2-Coloring
Odd Cycle Extraction O(V + E) O(V + E) BFS Tree + LCA Trace
Maximum Independent Set NP-Hard O(E √V) König's Theorem ($|V| - \text{MaxMatching}$)

Production Implementation: Multi-Component Bipartite Verifier

#include <vector>
#include <queue>
#include <cstdint>

struct BipartiteResult {
    bool isBipartite = true;
    std::vector<int8_t> color; // 0 or 1 if bipartite
};

BipartiteResult checkBipartite(int n, const std::vector<std::vector<int>>& adj) {
    BipartiteResult res{true, std::vector<int8_t>(n, -1)};
    std::queue<int> q;

    for (int start = 0; start < n; ++start) {
        if (res.color[start] != -1) continue;
        res.color[start] = 0;
        q.push(start);

        while (!q.empty()) {
            int u = q.front();
            q.pop();
            for (int v : adj[u]) {
                if (res.color[v] == -1) {
                    res.color[v] = res.color[u] ^ 1;
                    q.push(v);
                } else if (res.color[v] == res.color[u]) {
                    res.isBipartite = false;
                    return res;
                }
            }
        }
    }
    return res;
}
Failure Mode — Disconnected Components & Self-Loops

Two classic bugs break bipartite checkers in production: (1) Starting BFS only from vertex $0$ instead of looping over all unvisited vertices $0 \dots V-1$, which misses odd cycles in disconnected components; and (2) forgetting that a self-loop $(u, u)$ is a cycle of length $1$ (odd), which immediately invalidates bipartiteness.

Check yourself Question 1 of 4
Concept Check Structural Characterization of Bipartite Graphs recall
Section: König's Theorem

Which statement is a necessary and sufficient condition for an undirected graph $G$ to be bipartite?

⚡ Interactive Challenge · Staff Level

Algorithmic Design & Complexity Check

Analyze the invariant, asymptotic bound, and hardware behavior before selecting.

You are given an undirected graph $G = (V, E)$ ($|V| \le 2000$) and asked whether $V$ can be partitioned into two disjoint **cliques** (complete subgraphs). How can this be solved in $O(V^2)$ time?

Key Takeaways

König's 1916 Theorem proves that a graph is bipartite (2-colorable) if and only if it contains no cycle of odd length.
In a BFS spanning tree, every edge connecting two vertices at the same distance level d(r, u) == d(r, v) induces an odd cycle through their lowest common ancestor.
Always wrap BFS/DFS 2-coloring in an outer loop over all vertices 0..V-1 so disconnected components are verified.
Many NP-hard optimization problems (Maximum Independent Set, Minimum Vertex Cover) become polynomial-time solvable via Hopcroft-Karp once the graph is verified bipartite.
Was this lesson helpful? Spot an issue or have a suggestion? Let us know on GitHub.
Report an issue