From 09af5e3c0e6ff6575ee747c89f480c7f85945546 Mon Sep 17 00:00:00 2001 From: Anmol3299 Date: Tue, 2 Jun 2020 21:36:31 +0530 Subject: [PATCH 1/7] feat: Add BFS and DFS algorithms to check for cycle in a directed graph --- graph/cycle_check_directed_graph.cpp | 322 +++++++++++++++++++++++++++ 1 file changed, 322 insertions(+) create mode 100644 graph/cycle_check_directed_graph.cpp diff --git a/graph/cycle_check_directed_graph.cpp b/graph/cycle_check_directed_graph.cpp new file mode 100644 index 000000000..0cf5ac28e --- /dev/null +++ b/graph/cycle_check_directed_graph.cpp @@ -0,0 +1,322 @@ +/** + * Copyright 2020 + * @file cycle_check_directed graph.cpp + * + * @brief BFS and DFS algorithms to check for cycle in a directed graph. + * + * @author Anmol3299 + * contact: mittalanmol22@gmail.com + * + */ + +#include // for std::cout +#include // for std::queue +#include // for throwing errors +#include // for std::remove_reference_t +#include // for std::unordered_map +#include // for std::move +#include // for std::vector + +/** + * Implementation of non-weighted directed edge of a graph. + * + * The source vertex of the edge is labelled "src" and destination vertex is + * labelled "dest". + */ +struct Edge { + unsigned int src; + unsigned int dest; + + Edge() = delete; + ~Edge() = default; + Edge(Edge&&) = default; + Edge& operator=(Edge&&) = default; + Edge(Edge const&) = default; + Edge& operator=(Edge const&) = default; + + /** Set the source and destination of the vertex. + * + * @param source is the source vertex of the edge. + * @param destination is the destination vertex of the edge. + */ + Edge(unsigned int const& source, unsigned int const& destination) + : src(source), dest(destination) {} +}; + +using AdjList = std::unordered_map>; + +/** + * Implementation of graph class. + * + * The graph will be represented using Adjacency List representation. + * This class contains 2 data members "m_vertices" & "m_adjList" used to + * represent the number of vertices and adjacency list of the graph + * respectively. The vertices are labelled 0 - (m_vertices - 1). + */ +class Graph { + public: + Graph() : m_vertices(0), m_adjList({}) {} + ~Graph() = default; + Graph(Graph&&) = default; + Graph& operator=(Graph&&) = default; + Graph(Graph const&) = default; + Graph& operator=(Graph const&) = default; + + /** Create a graph from vertices and adjacency list. + * + * @param vertices specify the number of vertices the graph would contain. + * @param adjList is the adjacency list representation of graph. + */ + Graph(unsigned int const& vertices, AdjList const& adjList) + : m_vertices(vertices), m_adjList(adjList) {} + + /** Create a graph from vertices and adjacency list. + * + * @param vertices specify the number of vertices the graph would contain. + * @param adjList is the adjacency list representation of graph. + */ + Graph(unsigned int const& vertices, AdjList&& adjList) + : m_vertices(std::move(vertices)), m_adjList(std::move(adjList)) {} + + /** Create a graph from vertices and a set of edges. + * + * Adjacency list of the graph would be created from the set of edges. If + * the source or destination of any edge has a value greater or equal to + * number of vertices, then it would throw a range_error. + * + * @param vertices specify the number of vertices the graph would contain. + * @param edges is a vector of edges. + */ + Graph(unsigned int const& vertices, std::vector const& edges) + : m_vertices(vertices) { + for (auto const& edge : edges) { + if (edge.src >= vertices || edge.dest >= vertices) { + throw std::range_error( + "Either src or dest of edge out of range"); + } + m_adjList[edge.src].emplace_back(edge.dest); + } + } + + /** Create a graph from vertices and a set of edges. + * + * Adjacency list of the graph would be created from the set of edges. If + * the source or destination of any edge has a value greater or equal to + * number of vertices, then it would throw a range_error. + * + * @param vertices specify the number of vertices the graph would contain. + * @param edges is a vector of edges. + */ + Graph(unsigned int const& vertices, std::vector&& edges) + : m_vertices(vertices) { + for (auto&& edge : std::move(edges)) { + if (edge.src >= vertices || edge.dest >= vertices) { + throw std::range_error( + "Either src or dest of edge out of range"); + } + m_adjList[edge.src].emplace_back(std::move(edge.dest)); + } + } + + /** Return a const reference of the adjacency list. + * + * @return const reference to the adjacency list + */ + std::remove_reference_t const& getAdjList() const { + return m_adjList; + } + + /** + * @return number of vertices in the graph. + */ + std::remove_reference_t const& getVertices() const { + return m_vertices; + } + + /** Add vertices in the graph. + * + * @param num is the number of vertices to be added. It adds 1 vertex by + * default. + * + */ + void addVertices(unsigned int num = 1) { m_vertices += num; } + + /** Add an edge in the graph. + * + * @param edge that needs to be added. + */ + void addEdge(Edge const& edge) { + if (edge.src >= m_vertices || edge.dest >= m_vertices) { + throw std::range_error("Either src or dest of edge out of range"); + } + m_adjList[edge.src].emplace_back(edge.dest); + } + + /** Add an Edge in the graph + * + * @param source is source vertex of the edge. + * @param destination is the destination vertex of the edge. + */ + void addEdge(unsigned int const& source, unsigned int const& destination) { + if (source >= m_vertices || destination >= m_vertices) { + throw std::range_error( + "Either source or destination of edge out of range"); + } + m_adjList[source].emplace_back(destination); + } + + private: + unsigned int m_vertices; + AdjList m_adjList; +}; + +class CycleCheck { + private: + enum nodeStates : uint8_t { not_visited = 0, in_stack, visited }; + + /** Helper function of "isCyclicDFS". + * + * @param adjList is the adjacency list representation of some graph. + * @param state is the state of the nodes of the graph. + * @param node is the node being evaluated. + * + * @return true if graph has a cycle, else false. + */ + static bool isCyclicDFSHelper(AdjList const& adjList, + std::vector& state, + unsigned int node) { + // Add node "in_stack" state. + state[node] = in_stack; + + // If the node has children, then recursively visit all children of the + // node. + if (auto const& it = adjList.find(node); it != adjList.end()) { + for (auto child : it->second) { + // If state of child node is "not_visited", evaluate that child + // for presence of cycle. + if (auto state_of_child = state[child]; + state_of_child == not_visited) { + if (isCyclicDFSHelper(adjList, state, child)) { + return true; + } + } else if (state_of_child == in_stack) { + // If child node was "in_stack", then that means that there + // is a cycle in the graph. Return true for presence of the + // cycle. + return true; + } + } + } + + // Current node has been evaluated for the presence of cycle and had no + // cycle. Mark current node as "visited". + state[node] = visited; + // Return that current node didn't result in any cycles. + return false; + } + + public: + /** Driver function to check if a graph has a cycle. + * + * This function uses DFS to check for cycle in the graph. + * + * @param graph which needs to be evaluated for the presence of cycle. + * @return true if a cycle is detected, else false. + */ + static bool isCyclicDFS(Graph const& graph) { + /** State of the node. + * + * It is a vector of "nodeStates" which represents the state node is in. + * It can take only 3 values: "not_visited", "in_stack", and "visited". + * + * Initially, all nodes are in "not_visited" state. + */ + std::vector state(graph.getVertices(), not_visited); + + // Start visiting each node. + for (auto node = 0; node < graph.getVertices(); node++) { + // If a node is not visited, only then check for presence of cycle. + // There is no need to check for presence of cycle for a visited + // node as it has already been checked for presence of cycle. + if (state[node] == not_visited) { + // Check for cycle. + if (isCyclicDFSHelper(graph.getAdjList(), state, node)) { + return true; + } + } + } + + // All nodes have been safely traversed, that means there is no cycle in + // the graph. Return false. + return false; + } + + /** Check if a graph has cycle or not. + * + * This function uses BFS to check if a graph is cyclic or not. + * + * @param graph which needs to be evaluated for the presence of cycle. + * @return true if a cycle is detected, else false. + */ + static bool isCyclicBFS(Graph const& graph) { + AdjList graphAjdList = graph.getAdjList(); + + std::vector indegree(graph.getVertices(), 0); + // Calculate the indegree i.e. the number of incident edges to the node. + for (auto const& [parent, children] : graphAjdList) { + for (auto const& child : children) { + indegree[child]++; + } + } + + std::queue can_be_solved; + for (auto node = 0; node < graph.getVertices(); node++) { + // If a node doesn't have any input edges, then that node will + // definately not result in a cycle and can be visited safely. + if (!indegree[node]) { + can_be_solved.emplace(node); + } + } + + // Vertices that need to be traversed. + auto remain = graph.getVertices(); + // While there are safe nodes that we can visit. + while (!can_be_solved.empty()) { + auto front = can_be_solved.front(); + // Visit the node. + can_be_solved.pop(); + // Decrease number of nodes that need to be traversed. + remain--; + + // Visit all the children of the visited node. + if (auto it = graphAjdList.find(front); it != graphAjdList.end()) { + for (auto child : it->second) { + // Check if we can visited the node safely. + if (--indegree[child] == 0) { + // if node can be visited safely, then add that node to + // the visit queue. + can_be_solved.emplace(child); + } + } + } + } + + // If there are still nodes that we can't visit, then it means that + // there is a cycle and return true, else return false. + return !(remain == 0); + } +}; + +/** + * Main function. + */ +int main() { + // Instantiate the graph. + Graph g(7, {{0, 1}, {1, 2}, {2, 0}, {2, 5}, {3, 5}}); + // Check for cycle using BFS method. + std::cout << CycleCheck::isCyclicBFS(g) << '\n'; + + // Check for cycle using DFS method. + std::cout << CycleCheck::isCyclicDFS(g) << '\n'; + return 0; +} From c1fadaaeb39d52f0c64e53240b531daaaf355729 Mon Sep 17 00:00:00 2001 From: Anmol3299 Date: Wed, 3 Jun 2020 18:17:05 +0530 Subject: [PATCH 2/7] Remove const references for input of simple types Reason: overhead on access --- graph/cycle_check_directed_graph.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/graph/cycle_check_directed_graph.cpp b/graph/cycle_check_directed_graph.cpp index 0cf5ac28e..ba31b15c4 100644 --- a/graph/cycle_check_directed_graph.cpp +++ b/graph/cycle_check_directed_graph.cpp @@ -39,7 +39,7 @@ struct Edge { * @param source is the source vertex of the edge. * @param destination is the destination vertex of the edge. */ - Edge(unsigned int const& source, unsigned int const& destination) + Edge(unsigned int source, unsigned int destination) : src(source), dest(destination) {} }; @@ -67,7 +67,7 @@ class Graph { * @param vertices specify the number of vertices the graph would contain. * @param adjList is the adjacency list representation of graph. */ - Graph(unsigned int const& vertices, AdjList const& adjList) + Graph(unsigned int vertices, AdjList const& adjList) : m_vertices(vertices), m_adjList(adjList) {} /** Create a graph from vertices and adjacency list. @@ -75,7 +75,7 @@ class Graph { * @param vertices specify the number of vertices the graph would contain. * @param adjList is the adjacency list representation of graph. */ - Graph(unsigned int const& vertices, AdjList&& adjList) + Graph(unsigned int vertices, AdjList&& adjList) : m_vertices(std::move(vertices)), m_adjList(std::move(adjList)) {} /** Create a graph from vertices and a set of edges. @@ -87,7 +87,7 @@ class Graph { * @param vertices specify the number of vertices the graph would contain. * @param edges is a vector of edges. */ - Graph(unsigned int const& vertices, std::vector const& edges) + Graph(unsigned int vertices, std::vector const& edges) : m_vertices(vertices) { for (auto const& edge : edges) { if (edge.src >= vertices || edge.dest >= vertices) { @@ -107,7 +107,7 @@ class Graph { * @param vertices specify the number of vertices the graph would contain. * @param edges is a vector of edges. */ - Graph(unsigned int const& vertices, std::vector&& edges) + Graph(unsigned int vertices, std::vector&& edges) : m_vertices(vertices) { for (auto&& edge : std::move(edges)) { if (edge.src >= vertices || edge.dest >= vertices) { @@ -157,7 +157,7 @@ class Graph { * @param source is source vertex of the edge. * @param destination is the destination vertex of the edge. */ - void addEdge(unsigned int const& source, unsigned int const& destination) { + void addEdge(unsigned int source, unsigned int destination) { if (source >= m_vertices || destination >= m_vertices) { throw std::range_error( "Either source or destination of edge out of range"); From a3df421e36d4d6da66c1d116d184b7007e30ea57 Mon Sep 17 00:00:00 2001 From: Anmol3299 Date: Wed, 3 Jun 2020 18:53:01 +0530 Subject: [PATCH 3/7] fix bad code sorry for force push --- graph/cycle_check_directed_graph.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graph/cycle_check_directed_graph.cpp b/graph/cycle_check_directed_graph.cpp index ba31b15c4..0aebd3c2e 100644 --- a/graph/cycle_check_directed_graph.cpp +++ b/graph/cycle_check_directed_graph.cpp @@ -109,7 +109,7 @@ class Graph { */ Graph(unsigned int vertices, std::vector&& edges) : m_vertices(vertices) { - for (auto&& edge : std::move(edges)) { + for (auto&& edge : edges) { if (edge.src >= vertices || edge.dest >= vertices) { throw std::range_error( "Either src or dest of edge out of range"); From a77dafba6466222040c268851279f6ce1de2f558 Mon Sep 17 00:00:00 2001 From: Anmol3299 Date: Wed, 3 Jun 2020 19:40:51 +0530 Subject: [PATCH 4/7] Use pointer instead of the non-const reference because apparently google says so. --- graph/cycle_check_directed_graph.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/graph/cycle_check_directed_graph.cpp b/graph/cycle_check_directed_graph.cpp index 0aebd3c2e..cfc5f2b02 100644 --- a/graph/cycle_check_directed_graph.cpp +++ b/graph/cycle_check_directed_graph.cpp @@ -183,10 +183,10 @@ class CycleCheck { * @return true if graph has a cycle, else false. */ static bool isCyclicDFSHelper(AdjList const& adjList, - std::vector& state, + std::vector* state, unsigned int node) { // Add node "in_stack" state. - state[node] = in_stack; + (*state)[node] = in_stack; // If the node has children, then recursively visit all children of the // node. @@ -194,7 +194,7 @@ class CycleCheck { for (auto child : it->second) { // If state of child node is "not_visited", evaluate that child // for presence of cycle. - if (auto state_of_child = state[child]; + if (auto state_of_child = (*state)[child]; state_of_child == not_visited) { if (isCyclicDFSHelper(adjList, state, child)) { return true; @@ -210,7 +210,7 @@ class CycleCheck { // Current node has been evaluated for the presence of cycle and had no // cycle. Mark current node as "visited". - state[node] = visited; + (*state)[node] = visited; // Return that current node didn't result in any cycles. return false; } @@ -240,7 +240,7 @@ class CycleCheck { // node as it has already been checked for presence of cycle. if (state[node] == not_visited) { // Check for cycle. - if (isCyclicDFSHelper(graph.getAdjList(), state, node)) { + if (isCyclicDFSHelper(graph.getAdjList(), &state, node)) { return true; } } From 8f04ffe3fb36334f3be63c40104db34c8df6ca24 Mon Sep 17 00:00:00 2001 From: Anmol3299 Date: Wed, 3 Jun 2020 20:33:38 +0530 Subject: [PATCH 5/7] Remove a useless and possibly bad Graph constuctor overload --- graph/cycle_check_directed_graph.cpp | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/graph/cycle_check_directed_graph.cpp b/graph/cycle_check_directed_graph.cpp index cfc5f2b02..4a619850d 100644 --- a/graph/cycle_check_directed_graph.cpp +++ b/graph/cycle_check_directed_graph.cpp @@ -98,26 +98,6 @@ class Graph { } } - /** Create a graph from vertices and a set of edges. - * - * Adjacency list of the graph would be created from the set of edges. If - * the source or destination of any edge has a value greater or equal to - * number of vertices, then it would throw a range_error. - * - * @param vertices specify the number of vertices the graph would contain. - * @param edges is a vector of edges. - */ - Graph(unsigned int vertices, std::vector&& edges) - : m_vertices(vertices) { - for (auto&& edge : edges) { - if (edge.src >= vertices || edge.dest >= vertices) { - throw std::range_error( - "Either src or dest of edge out of range"); - } - m_adjList[edge.src].emplace_back(std::move(edge.dest)); - } - } - /** Return a const reference of the adjacency list. * * @return const reference to the adjacency list From 9d8736e79e435ef7c0208bbdb91e99fa56bd681d Mon Sep 17 00:00:00 2001 From: Anmol3299 Date: Wed, 3 Jun 2020 20:40:09 +0530 Subject: [PATCH 6/7] Explicitely specify type of vector during graph instantiation --- graph/cycle_check_directed_graph.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graph/cycle_check_directed_graph.cpp b/graph/cycle_check_directed_graph.cpp index 4a619850d..0f7b84cd3 100644 --- a/graph/cycle_check_directed_graph.cpp +++ b/graph/cycle_check_directed_graph.cpp @@ -292,7 +292,7 @@ class CycleCheck { */ int main() { // Instantiate the graph. - Graph g(7, {{0, 1}, {1, 2}, {2, 0}, {2, 5}, {3, 5}}); + Graph g(7, std::vector{{0, 1}, {1, 2}, {2, 0}, {2, 5}, {3, 5}}); // Check for cycle using BFS method. std::cout << CycleCheck::isCyclicBFS(g) << '\n'; From 6ebd88391f77c8fa7e5b2ef64cac303b8f4652d1 Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Wed, 3 Jun 2020 15:39:21 +0000 Subject: [PATCH 7/7] updating DIRECTORY.md --- DIRECTORY.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index 31146ab3d..12e22361d 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -49,6 +49,7 @@ * [Stack](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structure/stk/stack.h) * [Test Stack](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structure/stk/test_stack.cpp) * [Tree](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structure/tree.cpp) + * [Trie Modern](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structure/trie_modern.cpp) * [Trie Tree](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structure/trie_tree.cpp) ## Dynamic Programming @@ -80,6 +81,7 @@ * [Bridge Finding With Tarjan Algorithm](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/bridge_finding_with_tarjan_algorithm.cpp) * [Connected Components](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/connected_components.cpp) * [Connected Components With Dsu](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/connected_components_with_dsu.cpp) + * [Cycle Check Directed Graph](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/cycle_check_directed_graph.cpp) * [Dfs](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/dfs.cpp) * [Dfs With Stack](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/dfs_with_stack.cpp) * [Dijkstra](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/dijkstra.cpp)