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