Bron–Kerbosch algorithm

In computer science, the Bron–Kerbosch algorithm is an enumeration algorithm for finding all maximal cliques in an undirected graph. That is, it lists all subsets of vertices with the two properties that each pair of vertices in one of the listed subsets is connected by an edge, and no listed subset can have any additional vertices added to it while preserving its complete connectivity. The Bron–Kerbosch algorithm was designed by Dutch scientists Coenraad Bron and Joep Kerbosch, who published its description in 1973.

Although other algorithms for solving the clique problem have running times that are, in theory, better on inputs that have few maximal independent sets, the Bron–Kerbosch algorithm and subsequent improvements to it are frequently reported as being more efficient in practice than the alternatives. It is well-known and widely used in application areas of graph algorithms such as computational chemistry.

A contemporaneous algorithm of, although presented in different terms, can be viewed as being the same as the Bron–Kerbosch algorithm, as it generates the same search tree.

Without pivoting
The basic form of the Bron–Kerbosch algorithm is a recursive backtracking algorithm that searches for all maximal cliques in a given graph G. More generally, given three disjoint sets of vertices R, P, and X, it finds the maximal cliques that include all of the vertices in R, some of the vertices in P, and none of the vertices in X. In each call to the algorithm, P and X are disjoint sets whose union consists of those vertices that form cliques when added to R. In other words, P ∪ X is the set of vertices which are joined to every element of R. When P and X are both empty there are no further elements that can be added to R, so R is a maximal clique and the algorithm outputs R.

The recursion is initiated by setting R and X to be the empty set and P to be the vertex set of the graph. Within each recursive call, the algorithm considers the vertices in P in turn; if there are no such vertices, it either reports R as a maximal clique (if X is empty), or backtracks. For each vertex v chosen from P, it makes a recursive call in which v is added to R and in which P and X are restricted to the neighbor set N(v) of v, which finds and reports all clique extensions of R that contain v. Then, it moves v from P to X to exclude it from consideration in future cliques and continues with the next vertex in P.

That is, in pseudocode, the algorithm performs the following steps:

algorithm BronKerbosch1(R, P, X) is if P and X are both empty then report R as a maximal clique for each vertex v in P do BronKerbosch1(R ⋃ {v}, P ⋂ N(v), X ⋂ N(v)) P := P \ {v} X := X ⋃ {v}

With pivoting
The basic form of the algorithm, described above, is inefficient in the case of graphs with many non-maximal cliques: it makes a recursive call for every clique, maximal or not. To save time and allow the algorithm to backtrack more quickly in branches of the search that contain no maximal cliques, Bron and Kerbosch introduced a variant of the algorithm involving a "pivot vertex" u, chosen from P (or more generally, as later investigators realized, from P ⋃ X). Then, neighbors of that pivot element are not recursively tested. Any maximal clique potentially found in tests of neighbors of u would also be found when testing u or one of its non-neighbors, as at least one of these will always be a part of that maximal clique. Otherwise, only neighbors of u would be part of that maximal clique, allowing it to be augmented by adding u to it, which contradicts that clique being maximal. Therefore, only u and its non-neighbors need to be tested as the choices for the vertex v that is added to R in each recursive call to the algorithm. In pseudocode:

algorithm BronKerbosch2(R, P, X) is if P and X are both empty then report R as a maximal clique choose a pivot vertex u in P ⋃ X for each vertex v in P \ N(u) do BronKerbosch2(R ⋃ {v}, P ⋂ N(v), X ⋂ N(v)) P := P \ {v} X := X ⋃ {v}

If the pivot is chosen to minimize the number of recursive calls made by the algorithm, the savings in running time compared to the non-pivoting version of the algorithm can be significant.

With vertex ordering
An alternative method for improving the basic form of the Bron–Kerbosch algorithm involves forgoing pivoting at the outermost level of recursion, and instead choosing the ordering of the recursive calls carefully in order to minimize the sizes of the sets $P$ of candidate vertices within each recursive call.

The degeneracy of a graph $G$ is the smallest number $d$ such that every subgraph of $G$ has a vertex with degree $d$ or less. Every graph has a degeneracy ordering, an ordering of the vertices such that each vertex has $d$ or fewer neighbors that come later in the ordering; a degeneracy ordering may be found in linear time by repeatedly selecting the vertex of minimum degree among the remaining vertices. If the order of the vertices $v$ that the Bron–Kerbosch algorithm loops through is a degeneracy ordering, then the set $P$ of candidate vertices in each call (the neighbors of $v$ that are later in the ordering) will be guaranteed to have size at most $d$. The set $X$ of excluded vertices will consist of all earlier neighbors of $v$, and may be much larger than $d$. In recursive calls to the algorithm below the topmost level of the recursion, the pivoting version can still be used.

In pseudocode, the algorithm performs the following steps:

algorithm BronKerbosch3(G) is P = V(G) R = X = empty for each vertex v in a degeneracy ordering of G do BronKerbosch2({v}, P ⋂ N(v), X ⋂ N(v)) P := P \ {v} X := X ⋃ {v}

This variant of the algorithm can be proven to be efficient for graphs of small degeneracy, and experiments show that it also works well in practice for large sparse social networks and other real-world graphs.

Example
In the example graph shown, the algorithm is initially called with R = Ø, P = {1,2,3,4,5,6}, and X = Ø. The pivot u should be chosen as one of the degree-three vertices, to minimize the number of recursive calls; for instance, suppose that u is chosen to be vertex 2. Then there are three remaining vertices in P \ N(u): vertices 2, 4, and 6.

The iteration of the inner loop of the algorithm for v = 2 makes a recursive call to the algorithm with R = {2}, P = {1,3,5}, and X = Ø. Within this recursive call, one of 1 or 5 will be chosen as a pivot, and there will be two second-level recursive calls, one for vertex 3 and the other for whichever vertex was not chosen as pivot. These two calls will eventually report the two cliques {1,2,5} and {2,3}. After returning from these recursive calls, vertex 2 is added to X and removed from P.

The iteration of the inner loop of the algorithm for v = 4 makes a recursive call to the algorithm with R = {4}, P = {3,5,6}, and X = Ø (although vertex 2 belongs to the set X in the outer call to the algorithm, it is not a neighbor of v and is excluded from the subset of X passed to the recursive call). This recursive call will end up making three second-level recursive calls to the algorithm that report the three cliques {3,4}, {4,5}, and {4,6}. Then, vertex 4 is added to X and removed from P.

In the third and final iteration of the inner loop of the algorithm, for v = 6, there is a recursive call to the algorithm with R = {6}, P = Ø, and X = {4}. Because this recursive call has P empty and X non-empty, it immediately backtracks without reporting any more cliques, as there can be no maximal clique that includes vertex 6 and excludes vertex 4.

The call tree for the algorithm, therefore, looks like:

BronKerbosch2(Ø, {1,2,3,4,5,6}, Ø) BronKerbosch2({2}, {1,3,5}, Ø) BronKerbosch2({2,3}, Ø, Ø): output {2, 3} BronKerbosch2({2,5}, {1}, Ø) BronKerbosch2({1,2,5}, Ø, Ø): output {1,2,5} BronKerbosch2({4}, {3,5,6}, Ø) BronKerbosch2({3,4}, Ø, Ø): output {3,4} BronKerbosch2({4,5}, Ø, Ø): output {4,5} BronKerbosch2({4,6}, Ø, Ø): output {4,6} BronKerbosch2({6}, Ø, {4}): no output

The graph in the example has degeneracy two; one possible degeneracy ordering is 6,4,3,1,2,5. If the vertex-ordering version of the Bron–Kerbosch algorithm is applied to the vertices, in this order, the call tree looks like

BronKerbosch3(G) BronKerbosch2({6}, {4}, Ø) BronKerbosch2({6,4}, Ø, Ø): output {6,4} BronKerbosch2({4}, {3,5}, {6}) BronKerbosch2({4,3}, Ø, Ø): output {4,3} BronKerbosch2({4,5}, Ø, Ø): output {4,5} BronKerbosch2({3}, {2}, {4}) BronKerbosch2({3,2}, Ø, Ø): output {3,2} BronKerbosch2({1}, {2,5}, Ø) BronKerbosch2({1,2}, {5}, Ø) BronKerbosch2({1,2,5}, Ø, Ø): output {1,2,5} BronKerbosch2({2}, {5}, {1,3}): no output BronKerbosch2({5}, Ø, {1,2,4}): no output

Worst-case analysis
The Bron–Kerbosch algorithm is not an output-sensitive algorithm: unlike some other algorithms for the clique problem, it does not run in polynomial time per maximal clique generated. However, it is efficient in a worst-case sense: by a result of, any n-vertex graph has at most 3n/3 maximal cliques, and the worst-case running time of the Bron–Kerbosch algorithm (with a pivot strategy that minimizes the number of recursive calls made at each step) is O(3n/3), matching this bound.

For sparse graphs, tighter bounds are possible. In particular the vertex-ordering version of the Bron–Kerbosch algorithm can be made to run in time $O(dn3^{d/3})$, where $d$ is the degeneracy of the graph, a measure of its sparseness. There exist $d$-degenerate graphs for which the total number of maximal cliques is $(n &minus; d)3^{d/3}$, so this bound is close to tight.