Purpose: Enumerate all maximal cliques of an undirected graph. With pivoting, it runs in β€” which is optimal, because a graph can have that many maximal cliques (Moon-Moser).

Algorithm

Recursive backtracking on three sets:

  • β€” the clique built so far,
  • β€” candidates that can still extend ,
  • β€” vertices already processed (used to avoid reporting the same clique twice).
BronKerbosch(R, P, X):
    if P and X are both empty:
        report R as a maximal clique
    choose a pivot u from P βˆͺ X maximising |P ∩ N(u)|
    for each v in P \ N(u):
        BronKerbosch(R βˆͺ {v}, P ∩ N(v), X ∩ N(v))
        P ← P \ {v}
        X ← X βˆͺ {v}

Why the pivot. Any maximal clique must contain either or a vertex not adjacent to . So it suffices to branch on , and choosing to maximise makes that set as small as possible. Without pivoting the algorithm still works but is dramatically slower.

Code

Bitset version β€” the one to use, since all set operations become single machine words:

int n;
vector<unsigned long long> adj;   // adj[v] = bitmask of neighbours (n <= 64)
vector<unsigned long long> cliques;
 
void bk(unsigned long long R, unsigned long long P, unsigned long long X) {
    if (!P && !X) { cliques.push_back(R); return; }
 
    // pivot: vertex of P|X with the most neighbours inside P
    int pivot = -1, best = -1;
    unsigned long long PX = P | X;
    for (unsigned long long t = PX; t; t &= t - 1) {
        int u = __builtin_ctzll(t);
        int c = __builtin_popcountll(P & adj[u]);
        if (c > best) { best = c; pivot = u; }
    }
 
    unsigned long long cand = P & ~adj[pivot];
    for (unsigned long long t = cand; t; t &= t - 1) {
        int v = __builtin_ctzll(t);
        bk(R | (1ULL << v), P & adj[v], X & adj[v]);
        P &= ~(1ULL << v);
        X |= (1ULL << v);
    }
}

Paradigm

Backtracking with pruning. prunes duplicates; the pivot prunes branches.

Complexity

  • Time: worst case with pivoting β€” matching the maximum possible number of maximal cliques
  • With a degeneracy ordering at the top level: where is the graph degeneracy β€” near-linear on sparse real-world graphs
  • Space: for the adjacency bitsets, recursion depth

Correctness

Every reported is a clique by construction (each added is adjacent to all of ). It is maximal because means no vertex is adjacent to all of . No clique is reported twice: once moves from to , any later branch containing hits a non-empty at its would-be reporting point and is suppressed. Completeness follows from the pivot argument above. ∎

Variants / Use Cases

  • Maximum clique β€” take the largest reported clique, or use a dedicated branch-and-bound solver with a colouring bound (much faster when you only need the maximum)
  • Maximum independent set β€” run on the complement graph
  • Minimum vertex cover β€” minus the maximum independent set (KΓΆnig’s theorem gives a flow solution in bipartite graphs)
  • Graph colouring lower bounds β€” the clique number bounds the chromatic number from below
  • Community detection / social network analysis β€” the original motivation for degeneracy-ordered Bron-Kerbosch
  • Maximum Clique and Independent Set β€” the topic page