From 06b6714b0e3acc934518f257be3adc6e5694f4f8 Mon Sep 17 00:00:00 2001 From: Krishna Pal Deora <53469625+Krishnapal4050@users.noreply.github.com> Date: Fri, 16 Oct 2020 18:11:51 +0530 Subject: [PATCH] =?UTF-8?q?Hopcroft=E2=80=93Karp=20algorithm=20implementat?= =?UTF-8?q?ion=20(#1087)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Hopcroft–Karp algorithm implementation The Hopcroft–Karp algorithm is an algorithm that takes as input a bipartite graph and produces as output a maximum cardinality matching. * Update hopcroft_karp.cpp * fix : fixed the issues * Update graph/hopcroft_karp.cpp Co-authored-by: David Leal * Update graph/hopcroft_karp.cpp Co-authored-by: David Leal * Update hopcroft_karp.cpp Added Global variable as private variable * Update hopcroft_karp.cpp * Update hopcroft_karp.cpp * Update hopcroft_karp.cpp * Update hopcroft_karp.cpp * Update hopcroft_karp.cpp * Update graph/hopcroft_karp.cpp Co-authored-by: David Leal * Update graph/hopcroft_karp.cpp Co-authored-by: David Leal * Update graph/hopcroft_karp.cpp Co-authored-by: David Leal * Update graph/hopcroft_karp.cpp Co-authored-by: David Leal * Update graph/hopcroft_karp.cpp Co-authored-by: David Leal * Update graph/hopcroft_karp.cpp Co-authored-by: David Leal * Update graph/hopcroft_karp.cpp Co-authored-by: David Leal * Update hopcroft_karp.cpp * Update graph/hopcroft_karp.cpp Co-authored-by: David Leal * Update graph/hopcroft_karp.cpp Co-authored-by: David Leal * Update graph/hopcroft_karp.cpp Co-authored-by: David Leal * Update graph/hopcroft_karp.cpp Co-authored-by: David Leal * Update graph/hopcroft_karp.cpp Co-authored-by: David Leal * Update graph/hopcroft_karp.cpp Co-authored-by: David Leal * Update graph/hopcroft_karp.cpp Co-authored-by: David Leal * updating DIRECTORY.md * Update graph/hopcroft_karp.cpp Co-authored-by: David Leal * clang-tidy fixes for 780580f71fd7245da9ec27aa54c35fca6793883d * Update graph/hopcroft_karp.cpp Co-authored-by: David Leal * clang-tidy fixes for 03f97cb241e520c6ed6bb4427ca9977c26c303c6 * Update graph/hopcroft_karp.cpp Co-authored-by: David Leal * Update graph/hopcroft_karp.cpp Co-authored-by: David Leal * Update graph/hopcroft_karp.cpp Co-authored-by: David Leal * Update graph/hopcroft_karp.cpp Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> * Update graph/hopcroft_karp.cpp Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> * Update graph/hopcroft_karp.cpp Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> * Update graph/hopcroft_karp.cpp Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> * applied suggested changes Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> * Update graph/hopcroft_karp.cpp Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> * applied changes Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> * Update graph/hopcroft_karp.cpp Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> * improved documentation Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> * Update graph/hopcroft_karp.cpp Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> * Update graph/hopcroft_karp.cpp Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> * Update hopcroft_karp.cpp * corrected code * Update hopcroft_karp.cpp * changed the class name * applied suggested changes included the HKGraph class and it's member functions inside namespace graph * Update graph/hopcroft_karp.cpp Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> * Update hopcroft_karp.cpp * Update hopcroft_karp.cpp * added sample test cases * Update DIRECTORY.md Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> * Update DIRECTORY.md Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> * updating DIRECTORY.md Co-authored-by: David Leal Co-authored-by: @8848hg <53469557+Himalay12@users.noreply.github.com> Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> --- DIRECTORY.md | 1 + graph/hopcroft_karp.cpp | 325 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 326 insertions(+) create mode 100644 graph/hopcroft_karp.cpp diff --git a/DIRECTORY.md b/DIRECTORY.md index 7e72b439e..c431979aa 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -84,6 +84,7 @@ * [Depth First Search With Stack](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/depth_first_search_with_stack.cpp) * [Dijkstra](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/dijkstra.cpp) * [Hamiltons Cycle](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/hamiltons_cycle.cpp) + * [Hopcroft Karp](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/hopcroft_karp.cpp) * [Is Graph Bipartite](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/is_graph_bipartite.cpp) * [Kosaraju](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/kosaraju.cpp) * [Kruskal](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/kruskal.cpp) diff --git a/graph/hopcroft_karp.cpp b/graph/hopcroft_karp.cpp new file mode 100644 index 000000000..d4e002948 --- /dev/null +++ b/graph/hopcroft_karp.cpp @@ -0,0 +1,325 @@ +/** + * @file + * @brief Implementation of [Hopcroft–Karp](https://en.wikipedia.org/wiki/Hopcroft%E2%80%93Karp_algorithm) algorithm. + * @details + * The Hopcroft–Karp algorithm is an algorithm that takes as input a bipartite graph + * and produces as output a maximum cardinality matching, it runs in O(E√V) time in worst case. + * + * ### Bipartite graph + * A bipartite graph (or bigraph) is a graph whose vertices can be divided into two disjoint + * and independent sets U and V such that every edge connects a vertex in U to one in V. + * Vertex sets U and V are usually called the parts of the graph. + * Equivalently, a bipartite graph is a graph that does not contain any odd-length cycles. + * + * ### Matching and Not-Matching edges + * Given a matching M, edges that are part of matching are called Matching edges and edges that are not part + * of M (or connect free nodes) are called Not-Matching edges. + * + * ### Maximum cardinality matching + * Given a bipartite graphs G = ( V = ( X , Y ) , E ) whose partition has the parts X and Y, + * with E denoting the edges of the graph, the goal is to find a matching with as many edges as possible. + * Equivalently, a matching that covers as many vertices as possible. + * + * ### Augmenting paths + * Given a matching M, an augmenting path is an alternating path that starts from and ends on free vertices. + * All single edge paths that start and end with free vertices are augmenting paths. + * + * + * ### Concept + * A matching M is not maximum if there exists an augmenting path. It is also true other way, + * i.e, a matching is maximum if no augmenting path exists. + * + * + * ### Algorithm + * 1) Initialize the Maximal Matching M as empty. + * 2) While there exists an Augmenting Path P + * Remove matching edges of P from M and add not-matching edges of P to M + * (This increases size of M by 1 as P starts and ends with a free vertex + * i.e. a node that is not part of matching.) + * 3) Return M. + * + * + * + * @author [Krishna Pal Deora](https://github.com/Krishnapal4050) + * + */ + + +#include +#include +#include +#include +#include +#include +#include + +/** + * @namespace graph + * @brief Graph algorithms + */ + namespace graph { + +/** + * @brief Represents Bipartite graph for + * Hopcroft Karp implementation + */ +class HKGraph +{ + int m{}; ///< m is the number of vertices on left side of Bipartite Graph + int n{}; ///< n is the number of vertices on right side of Bipartite Graph + const int NIL{0}; + const int INF{INT_MAX}; + + std::vector >adj; ///< adj[u] stores adjacents of left side and 0 is used for dummy vertex + + std::vector pair_u; ///< value of vertex 'u' ranges from 1 to m + std::vector pair_v; ///< value of vertex 'v' ranges from 1 to n + std::vector dist; ///< dist represents the distance between vertex 'u' and vertex 'v' + +public: + HKGraph(); // Default Constructor + HKGraph(int m, int n); // Constructor + void addEdge(int u, int v); // To add edge + + bool bfs(); // Returns true if there is an augmenting path + bool dfs(int u); // Adds augmenting path if there is one beginning with u + + int hopcroftKarpAlgorithm(); // Returns size of maximum matching +}; + + +/** + * @brief This function counts the number of augmenting paths between left and right sides of the Bipartite graph + * @returns size of maximum matching + */ +int HKGraph::hopcroftKarpAlgorithm() +{ + + // pair_u[u] stores pair of u in matching on left side of Bipartite Graph. + // If u doesn't have any pair, then pair_u[u] is NIL + pair_u = std::vector(m + 1,NIL); + + // pair_v[v] stores pair of v in matching on right side of Biparite Graph. + // If v doesn't have any pair, then pair_u[v] is NIL + pair_v = std::vector(n + 1,NIL); + + dist = std::vector(m + 1); // dist[u] stores distance of left side vertices + + int result = 0; // Initialize result + + // Keep updating the result while there is an augmenting path possible. + while (bfs()) + { + // Find a free vertex to check for a matching + for (int u = 1; u <= m; u++){ + + // If current vertex is free and there is + // an augmenting path from current vertex + // then increment the result + if (pair_u[u] == NIL && dfs(u)){ + result++; + } + } + } + return result; +} + + +/** + * @brief This function checks for the possibility of augmented path availability + * @returns `true` if there is an augmenting path available + * @returns `false` if there is no augmenting path available + */ +bool HKGraph::bfs() +{ + std::queue q; // an integer queue for bfs + + // First layer of vertices (set distance as 0) + for (int u = 1; u <= m; u++) + { + // If this is a free vertex, add it to queue + if (pair_u[u] == NIL){ + + dist[u] = 0; // u is not matched so distance is 0 + q.push(u); + } + + else{ + dist[u] = INF; // set distance as infinite so that this vertex is considered next time for availibility + } + } + + + dist[NIL] = INF; // Initialize distance to NIL as infinite + + // q is going to contain vertices of left side only. + while (!q.empty()) + { + int u = q.front(); // dequeue a vertex + q.pop(); + + // If this node is not NIL and can provide a shorter path to NIL then + if (dist[u] < dist[NIL]) + { + // Get all the adjacent vertices of the dequeued vertex u + std::list::iterator it; + for (it = adj[u].begin(); it != adj[u].end(); ++it) + { + int v = *it; + + // If pair of v is not considered so far i.e. (v, pair_v[v]) is not yet explored edge. + if (dist[pair_v[v]] == INF) + { + dist[pair_v[v]] = dist[u] + 1; + q.push(pair_v[v]); // Consider the pair and push it to queue + } + } + } + } + + + + return (dist[NIL] != INF); // If we could come back to NIL using alternating path of distinct vertices then there is an augmenting path available +} + +/** + * @brief This functions checks whether an augmenting path is available exists beginning with free vertex u + * @param u represents position of vertex + * @returns `true` if there is an augmenting path beginning with free vertex u + * @returns `false` if there is no augmenting path beginning with free vertex u + */ +bool HKGraph::dfs(int u) +{ + if (u != NIL) + { + std::list::iterator it; + for (it = adj[u].begin(); it != adj[u].end(); ++it) + { + + int v = *it; // Adjacent vertex of u + + // Follow the distances set by BFS search + if (dist[pair_v[v]] == dist[u] + 1) + { + // If dfs for pair of v also return true then new matching possible, store the matching + if (dfs(pair_v[v]) == true) + { + pair_v[v] = u; + pair_u[u] = v; + return true; + } + } + } + + + dist[u] = INF; // If there is no augmenting path beginning with u then set distance to infinite. + return false; + } + return true; +} + +/** + * @brief Default Constructor for initialization + */ +HKGraph::HKGraph() = default; + +/** + * @brief Constructor for initialization + * @param m is the number of vertices on left side of Bipartite Graph + * @param n is the number of vertices on right side of Bipartite Graph + */ +HKGraph::HKGraph(int m, int n) { + this->m = m; + this->n = n; + adj = std::vector >(m + 1); +} + +/** + * @brief function to add edge from u to v + * @param u is the position of first vertex + * @param v is the position of second vertex + */ +void HKGraph::addEdge(int u, int v) +{ + adj[u].push_back(v); // Add v to u’s list. +} + +} // namespace graph + +using graph::HKGraph; + +/** + * Self-test implementation + * @returns none + */ +void tests(){ + // Sample test case 1 + int v1a = 3, v1b = 5, e1 = 2; // vertices of left side, right side and edges + HKGraph g1(v1a, v1b); // execute the algorithm + + g1.addEdge(0,1); + g1.addEdge(1,4); + + int expected_res1 = 0; // for the above sample data, this is the expected output + int res1 = g1.hopcroftKarpAlgorithm(); + + assert(res1 == expected_res1); // assert check to ensure that the algorithm executed correctly for test 1 + + // Sample test case 2 + int v2a = 4, v2b = 4, e2 = 6; // vertices of left side, right side and edges + HKGraph g2(v2a, v2b); // execute the algorithm + + g2.addEdge(1,1); + g2.addEdge(1,3); + g2.addEdge(2,3); + g2.addEdge(3,4); + g2.addEdge(4,3); + g2.addEdge(4,2); + + int expected_res2 = 0; // for the above sample data, this is the expected output + int res2 = g2.hopcroftKarpAlgorithm(); + + assert(res2 == expected_res2); // assert check to ensure that the algorithm executed correctly for test 2 + + // Sample test case 3 + int v3a = 6, v3b = 6, e3 = 4; // vertices of left side, right side and edges + HKGraph g3(v3a, v3b); // execute the algorithm + + g3.addEdge(0,1); + g3.addEdge(1,4); + g3.addEdge(1,5); + g3.addEdge(5,0); + + int expected_res3 = 0; // for the above sample data, this is the expected output + int res3 = g3.hopcroftKarpAlgorithm(); + + assert(res3 == expected_res3); // assert check to ensure that the algorithm executed correctly for test 3 + + + +} + +/** + * @brief Main function + * @returns 0 on exit + */ +int main() +{ + tests(); // perform self-tests + + int v1 = 0, v2 = 0, e = 0; + std::cin >> v1 >> v2 >> e; // vertices of left side, right side and edges + HKGraph g(v1, v2); + int u = 0, v = 0; + for (int i = 0; i < e; ++i) + { + std::cin >> u >> v; + g.addEdge(u, v); + } + + int res = g.hopcroftKarpAlgorithm(); + std::cout << "Maximum matching is " << res <<"\n"; + + return 0; + +}