 GeeksforGeeks App
Open App Browser
Continue

# Find the Number of Cliques in a Graph

In graph theory, a clique is a subset of vertices of an undirected graph such that every two distinct vertices in the clique are adjacent, that is, they are connected by an edge of the graph. The number of cliques in a graph is the total number of cliques that can be found in the graph.

The Mathematics behind cliques in a graph involves the concept of adjacency matrices and graph theory. An adjacency matrix is a matrix representation of a graph where each row and column corresponds to a vertex in the graph and the elements of the matrix indicate whether there is an edge between the vertices.

For example, in an undirected graph with 5 vertices, the adjacency matrix would be a 5×5 matrix where a 1 in position (i, j) indicates that there is an edge between vertex i and vertex j, and a 0 indicates that there is no edge.

1 1 1 0 0
1 1 1 0 0
1 1 1 1 1
0 0 1 1 1
0 0 1 1 1

To find the number of cliques in a graph using an adjacency matrix, you can use a graph algorithm such as the Bron–Kerbosch algorithm, which is an efficient method for enumerating all cliques in an undirected graph.

The Bron–Kerbosch algorithm works by iterating over all possible subsets of the vertices and checking if they form a clique. It does this by using the adjacency matrix to check if every pair of vertices in the subset is adjacent (i.e., connected by an edge). If they are, then the subset forms a clique and is added to the list of cliques.

Example 1:

Consider the following graph: Example 1

This graph has three cliques: {1, 2, 3}, {3, 4, 5}, and {1, 2, 4, 5}.

To find the number of cliques in a graph, graph traversal algorithms such as depth-first search (DFS) or breadth-first search (BFS) can be used to visit all the vertices and check for cliques at each vertex.

For example, you could start at vertex 1 and perform a DFS to explore the graph. When you reach vertex 3, you know that the vertices 1, 2, and 3 form a clique. You can then continue the DFS to explore the remaining vertices and check for cliques at each vertex.

Alternatively, you could use a graph algorithm specifically designed to find cliques, such as the Bron–Kerbosch algorithm. This algorithm is an efficient method for enumerating all cliques in an undirected graph.

Example 2:

Consider a simple undirected graph with 4 vertices and 6 edges, as shown below: Example 2

To count the number of cliques in this graph, we can use the following formula:

Number of cliques = n * (n – 1) / 2 – m + 1 where n is the number of vertices in the graph and m is the number of edges. Plugging in the values for this graph, we get:
Number of cliques = 4 * (4 – 1) / 2 – 6 + 1 = 2

So there are 2 cliques in this graph.

This formula works because it counts the number of possible pairs of vertices in the graph (n * (n – 1) / 2), and then subtracts the number of edges to account for overcounting. Finally, it adds 1 to account for the fact that a single vertex on its own is also considered a clique.

This formula only works for undirected graphs, and it may not give the correct result for graphs with multiple edges or self-loops. It is also not practical for large graphs, as the time complexity of this approach is O(n^2). However, it can be a useful tool for quickly counting the number of cliques in small graphs

### Approaches:

• Brute-force search: One approach is to simply enumerate all possible cliques in the graph and count them. This approach has a time complexity of O(3^n), where n is the number of vertices in the graph, so it is only practical for very small graphs.
• Bron-Kerbosch algorithm: The Bron-Kerbosch algorithm is a pivot-based algorithm that uses a recursive approach to find all cliques in a graph. It has a time complexity of O(3^(n/3)), where n is the number of vertices in the graph, and a space complexity of O(n).
• Tomita algorithm: The Tomita algorithm is another pivot-based algorithm that uses a recursive approach to find all cliques in a graph. It has a time complexity of O(4^(n/4)), where n is the number of vertices in the graph and a space complexity of O(n).
• Pivot Bron-Kerbosch algorithm: The pivot Bron-Kerbosch algorithm is an improvement over the Bron-Kerbosch algorithm that uses a pivot element to prune the search space and reduce the time complexity. It has a time complexity of O(2^n), where n is the number of vertices in the graph, and a space complexity of O(n^2).
• Hybrid algorithm: The hybrid algorithm is a combination of the Bron-Kerbosch and Tomita algorithms that uses both pivoting and recursion to find all cliques in a graph. It has a time complexity of O(2^n), where n is the number of vertices in the graph and a space complexity of O(n^2)
• Approximation algorithms: Another approach is to use approximation algorithms, which are designed to find a good approximation of the number of cliques in a graph in polynomial time. These algorithms may not find all cliques in the graph, but they can be useful in cases where the exact number of cliques is not necessary.
• Parallelization techniques: It is also possible to improve the performance of the above algorithms by using parallelization techniques, such as multi-threading or distributed computing, to divide the work across multiple processors. However, these approaches may have additional overhead costs and may not be suitable for all types of graphs.

Which approach is best for your specific problem will depend on the size of the graph and the desired time and space complexity. You should choose the approach that best meets your needs and constraints.

### Observation behind the Approaches:

The different approaches to finding the number of cliques in a graph are based on different observations and techniques. Here are some observations behind these approaches:

• Brute-force search: This approach simply enumerates all possible cliques in the graph and counts them. It is based on the observation that a clique is a subset of the vertices of the graph that are all connected to each other.
• Bron-Kerbosch algorithm: The Bron-Kerbosch algorithm is based on the observation that a clique can be found by starting with a vertex and then expanding to include its neighbors, as long as they are all connected to each other. The algorithm uses a pivot element to prune the search space and reduce the time complexity.
• Tomita algorithm: The Tomita algorithm is based on the observation that a clique can be found by starting with a vertex and then expanding to include its neighbors, as long as they are all connected to each other. The algorithm uses a pivot element to prune the search space and reduce the time complexity.
• Pivot Bron-Kerbosch algorithm: The pivot Bron-Kerbosch algorithm is an improvement over the Bron-Kerbosch algorithm that uses a pivot element to prune the search space and reduce the time complexity. It is based on the observation that many cliques in a graph share common vertices, and by using a pivot element to focus the search on these vertices, it is possible to reduce the time complexity of the algorithm.
• Hybrid algorithm: The hybrid algorithm combines the Bron-Kerbosch and Tomita algorithms and uses both pivoting and recursion to find all cliques in a graph. It is based on the observation that both of these algorithms are effective at finding cliques, and by combining them, it is possible to find all cliques in a graph more efficiently.
• Approximation algorithms: Approximation algorithms are based on the observation that it is often sufficient to find a good approximation of the number of cliques in a graph, rather than the exact number. These algorithms use techniques such as random sampling or graph partitioning to find a good approximation in polynomial time.
• Parallelization techniques: Parallelization techniques are based on the observation that it is often possible to improve the performance of an algorithm by dividing the work across multiple processors. These techniques can be used to speed up the execution of the above algorithms by dividing the search space among multiple threads or processes

Here is a simple algorithm in Python to find the number of cliques in an undirected graph:

## Python3

 `# Python code``from` `collections ``import` `defaultdict`  `def` `find_cliques(graph):``    ``cliques ``=` `[]``    ``visited ``=` `set``()` `    ``def` `dfs(node, clique):``        ``visited.add(node)``        ``clique.add(node)` `        ``for` `neighbor ``in` `graph[node]:``            ``if` `neighbor ``not` `in` `visited:``                ``dfs(neighbor, clique)` `    ``for` `node ``in` `graph:``        ``if` `node ``not` `in` `visited:``            ``clique ``=` `set``()``            ``dfs(node, clique)``            ``if` `len``(clique) > ``1``:``                ``cliques.append(clique)` `    ``return` `cliques`  `# Example usage``graph ``=` `{``    ``'A'``: [``'B'``, ``'C'``, ``'D'``],``    ``'B'``: [``'A'``, ``'C'``, ``'D'``],``    ``'C'``: [``'A'``, ``'B'``, ``'D'``],``    ``'D'``: [``'A'``, ``'B'``, ``'C'``],``    ``'E'``: [``'F'``],``    ``'F'``: [``'E'``]``}` `cliques ``=` `find_cliques(graph)``print``(f``'Number of cliques: {len(cliques)}'``)``print``(f``'Cliques: {cliques}'``)`

## Java

 `import` `java.util.*;` `public` `class` `CliqueFinder {``    ``// Finds all cliques in a graph represented as a map``    ``// where the keys are nodes and the values are lists of neighbors``    ``static` `List> findCliques(Map> graph) {``        ``// List to store the cliques``        ``List> cliques = ``new` `ArrayList<>();``        ``// Set to keep track of visited nodes``        ``Set visited = ``new` `HashSet<>();` `        ``// Iterate over all nodes in the graph``        ``for` `(String node : graph.keySet()) {``            ``// Skip nodes that have already been visited``            ``if` `(!visited.contains(node)) {``                ``// Create a new clique``                ``Set clique = ``new` `HashSet<>();``                ``// Perform a depth-first search starting at the current node``                ``dfs(node, graph, visited, clique);``                ``// Add the clique to the list if it contains more than one node``                ``if` `(clique.size() > ``1``) {``                    ``cliques.add(clique);``                ``}``            ``}``        ``}` `        ``return` `cliques;``    ``}` `    ``// Performs a depth-first search from the given node``    ``static` `void` `dfs(String node, Map> graph, Set visited, Set clique) {``        ``// Mark the current node as visited``        ``visited.add(node);``        ``// Add the current node to the clique``        ``clique.add(node);` `        ``// Iterate over the neighbors of the current node``        ``for` `(String neighbor : graph.get(node)) {``            ``// Skip neighbors that have already been visited``            ``if` `(!visited.contains(neighbor)) {``                ``// Perform a depth-first search from the current neighbor``                ``dfs(neighbor, graph, visited, clique);``            ``}``        ``}``    ``}` `    ``public` `static` `void` `main(String[] args) {``        ``// Example usage``        ``Map> graph = ``new` `HashMap<>();``        ``graph.put(``"A"``, Arrays.asList(``"B"``, ``"C"``, ``"D"``));``        ``graph.put(``"B"``, Arrays.asList(``"A"``, ``"C"``, ``"D"``));``        ``graph.put(``"C"``, Arrays.asList(``"A"``, ``"B"``, ``"D"``));``        ``graph.put(``"D"``, Arrays.asList(``"A"``, ``"B"``, ``"C"``));``        ``graph.put(``"E"``, Arrays.asList(``"F"``));``        ``graph.put(``"F"``, Arrays.asList(``"E"``));` `        ``List> cliques = findCliques(graph);``        ``System.out.println(``"Number of cliques: "` `+ cliques.size());``        ``System.out.println(``"Cliques: "` `+ cliques);``    ``}``}`

## C++

 `#include ``#include ``#include ``#include ` `using` `namespace` `std;` `// Finds all cliques in a graph represented as a map``// where the keys are nodes and the values are lists of neighbors``vector> findCliques(map> graph) {``    ``// Vector to store the cliques``    ``vector> cliques;``    ``// Set to keep track of visited nodes``    ``set visited;` `    ``// Iterate over all nodes in the graph``    ``for` `(``auto` `node : graph) {``        ``// Skip nodes that have already been visited``        ``if` `(visited.find(node.first) == visited.end()) {``            ``// Create a new clique``            ``set clique;``            ``// Perform a depth-first search starting at the current node``            ``dfs(node.first, graph, visited, clique);``            ``// Add the clique to the vector if it contains more than one node``            ``if` `(clique.size() > 1) {``                ``cliques.push_back(clique);``            ``}``        ``}``    ``}` `    ``return` `cliques;``}` `// Performs a depth-first search from the given node``void` `dfs(string node, map> graph, set& visited, set& clique) {``    ``// Mark the current node as visited``    ``visited.insert(node);``    ``// Add the current node to the clique``    ``clique.insert(node);` `    ``// Iterate over the neighbors of the current node``    ``for` `(string neighbor : graph[node]) {``        ``// Skip neighbors that have already been visited``        ``if` `(visited.find(neighbor) == visited.end()) {``            ``// Perform a depth-first search from the current neighbor``            ``dfs(neighbor, graph, visited, clique);``        ``}``    ``}``}` `int` `main() {``    ``// Example usage``  ``map> graph;``    ``graph[``"A"``] = {``"B"``, ``"C"``, ``"D"``};``    ``graph[``"B"``] = {``"A"``, ``"C"``, ``"D"``};``    ``graph[``"C"``] = {``"A"``, ``"B"``, ``"D"``};``    ``graph[``"D"``] = {``"A"``, ``"B"``, ``"C"``};``    ``graph[``"E"``] = {``"F"``};``    ``graph[``"F"``] = {``"E"``};` `    ``vector> cliques = findCliques(graph);``    ``cout << ``"Number of cliques: "` `<< cliques.size() << endl;``    ``cout << ``"Cliques: ["``;``    ``for``(``auto` `clique:cliques) {``        ``for``(``auto` `it:clique){``            ``cout << it << ``","``;``        ``}``        ``cout << ``"]"``;``    ``}``    ``return` `0;``}`

## C#

 `using` `System;``using` `System.Collections.Generic;` `class` `CliqueFinder {``    ``// Finds all cliques in a graph represented as a dictionary``    ``// where the keys are nodes and the values are lists of neighbors``    ``static` `List> FindCliques(Dictionary<``string``, List<``string``>> graph) {``        ``// List to store the cliques``        ``List> cliques = ``new` `List>();``        ``// Set to keep track of visited nodes``        ``HashSet<``string``> visited = ``new` `HashSet<``string``>();` `        ``// Iterate over all nodes in the graph``        ``foreach` `(``string` `node ``in` `graph.Keys) {``            ``// Skip nodes that have already been visited``            ``if` `(!visited.Contains(node)) {``                ``// Create a new clique``                ``HashSet<``string``> clique = ``new` `HashSet<``string``>();``                ``// Perform a depth-first search starting at the current node``                ``Dfs(node, graph, visited, clique);``                ``// Add the clique to the list if it contains more than one node``                ``if` `(clique.Count > 1) {``                    ``cliques.Add(clique);``                ``}``            ``}``        ``}` `        ``return` `cliques;``    ``}` `    ``// Performs a depth-first search from the given node``    ``static` `void` `Dfs(``string` `node, Dictionary<``string``, List<``string``>> graph, HashSet<``string``> visited, HashSet<``string``> clique) {``        ``// Mark the current node as visited``        ``visited.Add(node);``        ``// Add the current node to the clique``        ``clique.Add(node);` `        ``// Iterate over the neighbors of the current node``        ``foreach` `(``string` `neighbor ``in` `graph[node]) {``            ``// Skip neighbors that have already been visited``            ``if` `(!visited.Contains(neighbor)) {``                ``// Perform a depth-first search from the current neighbor``                ``Dfs(neighbor, graph, visited, clique);``            ``}``        ``}``    ``}` `    ``public` `static` `void` `Main(``string``[] args) {``        ``// Example usage``        ``Dictionary<``string``, List<``string``>> graph = ``new` `Dictionary<``string``, List<``string``>>();``        ``graph.Add(``"A"``, ``new` `List<``string``>() { ``"B"``, ``"C"``, ``"D"` `});``        ``graph.Add(``"B"``, ``new` `List<``string``>() { ``"A"``, ``"C"``, ``"D"` `});``        ``graph.Add(``"C"``, ``new` `List<``string``>() { ``"A"``, ``"B"``, ``"D"` `});``        ``graph.Add(``"D"``, ``new` `List<``string``>() { ``"A"``, ``"B"``, ``"C"` `});``        ``graph.Add(``"E"``, ``new` `List<``string``>() { ``"F"` `});``        ``graph.Add(``"F"``, ``new` `List<``string``>() { ``"E"` `});` `        ``List> cliques = FindCliques(graph);``        ``Console.WriteLine(``"Number of cliques: "` `+ cliques.Count);``        ``Console.WriteLine(``"Cliques: "` `+ cliques);``    ``}``}`

## Javascript

 `function` `findCliques(graph) {``    ``// List to store the cliques``    ``let cliques = [];``    ``// Set to keep track of visited nodes``    ``let visited = ``new` `Set();` `    ``// Iterate over all nodes in the graph``    ``for` `(let node ``in` `graph) {``        ``// Skip nodes that have already been visited``        ``if` `(!visited.has(node)) {``            ``// Create a new clique``            ``let clique = ``new` `Set();``            ``// Perform a depth-first search starting at the current node``            ``dfs(node, graph, visited, clique);``            ``// Add the clique to the list if it contains more than one node``            ``if` `(clique.size > 1) {``                ``cliques.push(clique);``            ``}``        ``}``    ``}` `    ``return` `cliques;``}` `// Performs a depth-first search from the given node``function` `dfs(node, graph, visited, clique) {``    ``// Mark the current node as visited``    ``visited.add(node);``    ``// Add the current node to the clique``    ``clique.add(node);` `    ``// Iterate over the neighbors of the current node``    ``for` `(let neighbor of graph[node]) {``        ``// Skip neighbors that have already been visited``        ``if` `(!visited.has(neighbor)) {``            ``// Perform a depth-first search from the current neighbor``            ``dfs(neighbor, graph, visited, clique);``        ``}``    ``}``}` `// Example usage``let graph = {``    ``'A'``: [``'B'``, ``'C'``, ``'D'``],``    ``'B'``: [``'A'``, ``'C'``, ``'D'``],``    ``'C'``: [``'A'``, ``'B'``, ``'D'``],``    ``'D'``: [``'A'``, ``'B'``, ``'C'``],``    ``'E'``: [``'F'``],``    ``'F'``: [``'E'``]``};` `let cliques = findCliques(graph);``console.log(`Number of cliques: \${cliques.length}`);``console.log(`Cliques: \${cliques}`);`

Output

```Number of cliques: 2
Cliques: [{'D', 'B', 'C', 'A'}, {'E', 'F'}]```

The time complexity of the find_cliques function provided in the code is O(n+m), where n is the number of vertices in the graph and m is the number of edges. This is because the function performs a depth-first search (DFS) on the graph, which takes time proportional to the number of vertices and
The auxiliary space of this function is O(n) because it uses a set to store the visited vertices and a list to store the cliques, both of which have sizes proportional to the number of vertices in the graph.

Note: that the time and space complexity of this function may be different if the graph is represented differently, for example as an adjacency matrix instead of an adjacency list. The time and space complexity of an algorithm can also depend on the specific implementation and the specific input.

### Time and space complexity of each approach:

• The Bron-Kerbosch algorithm and the Tomita algorithm are both pivot-based algorithms that use a recursive approach to find all cliques in a graph.
• The Pivot Bron-Kerbosch algorithm is an improvement over the Bron-Kerbosch algorithm that reduces the time complexity by using a pivot element to prune the search space.
• The hybrid algorithm is a combination of the Bron-Kerbosch and Tomita algorithms that uses both pivoting and recursion to find all cliques in a graph.
• It is worth noting that the time complexity of these algorithms can be improved by using parallelization techniques or approximative algorithms, but these approaches may not find all cliques in the graph and may have additional overhead costs.

My Personal Notes arrow_drop_up