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:
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 depthfirst search (DFS) or breadthfirst 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:
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 = 2So 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 selfloops. 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:
 Bruteforce 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.
 BronKerbosch algorithm: The BronKerbosch algorithm is a pivotbased 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 pivotbased 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 BronKerbosch algorithm: The pivot BronKerbosch algorithm is an improvement over the BronKerbosch 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 BronKerbosch 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 multithreading 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:
 Bruteforce 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.
 BronKerbosch algorithm: The BronKerbosch 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 BronKerbosch algorithm: The pivot BronKerbosch algorithm is an improvement over the BronKerbosch 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 BronKerbosch 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

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 depthfirst 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:
Approach  Time Complexity  Space Complexity 

BronKerbosch algorithm 
O(3^(n/3)) 
O(n) 
Tomita algorithm 
O(4^(n/4)) 
O(n) 
Pivot BronKerbosch algorithm 
O(2^n) 
O(2^n) 
Hybrid algorithm 
O(2^n) 
O(2^n) 
 The BronKerbosch algorithm and the Tomita algorithm are both pivotbased algorithms that use a recursive approach to find all cliques in a graph.
 The Pivot BronKerbosch algorithm is an improvement over the BronKerbosch algorithm that reduces the time complexity by using a pivot element to prune the search space.
 The hybrid algorithm is a combination of the BronKerbosch 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.