Skip to content

Commit 194d668

Browse files
authored
Introduced IsClyclic with DFS
1 parent 176d6b9 commit 194d668

File tree

3 files changed

+199
-5
lines changed

3 files changed

+199
-5
lines changed

CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
cmake_minimum_required(VERSION 3.9)
22

33
# set the project name and version
4-
project(CXXGraph VERSION 0.0.4)
4+
project(CXXGraph VERSION 0.0.5)
55

66
configure_file(CXXGraphConfig.h.in ${PROJECT_SOURCE_DIR}/include/CXXGraphConfig.h)
77

@@ -31,6 +31,7 @@ add_executable(test_exe test/main.cpp
3131
test/DijkstraTest.cpp
3232
test/BFSTest.cpp
3333
test/DFSTest.cpp
34+
test/CycleCheckTest.cpp
3435
)
3536
target_include_directories(test_exe PUBLIC
3637
"${PROJECT_SOURCE_DIR}/include"
@@ -53,4 +54,5 @@ add_test(test_graph test_exe --gtest_filter=GraphTest*)
5354
add_test(test_dijkstra test_exe --gtest_filter=DijkstraTest*)
5455
add_test(test_bfs test_exe --gtest_filter=BFSTest*)
5556
add_test(test_dfs test_exe --gtest_filter=DFSTest*)
57+
add_test(test_cycle_check test_exe --gtest_filter=CycleCheckTest*)
5658

include/Graph.hpp

Lines changed: 123 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,22 @@ namespace CXXGRAPH
509509
*/
510510
const std::vector<Node<T>> depth_first_search(const Node<T> &start) const;
511511

512+
/**
513+
* \brief
514+
* This function uses DFS to check for cycle in the graph.
515+
* Pay Attention, this function work only with directed Graph
516+
*
517+
* @return true if a cycle is detected, else false. ( false is returned also if the graph in indirected)
518+
*/
519+
const bool isCyclicDFS() const;
520+
/**
521+
* \brief
522+
* This function checks if a graph is directed
523+
*
524+
* @return true if the graph is directed, else false.
525+
*/
526+
const bool isDirectedGraph() const;
527+
512528
friend std::ostream &operator<<<>(std::ostream &os, const Graph<T> &graph);
513529
friend std::ostream &operator<<<>(std::ostream &os, const AdjacencyMatrix<T> &adj);
514530
};
@@ -767,9 +783,10 @@ namespace CXXGRAPH
767783
{
768784
return visited;
769785
}
770-
const AdjacencyMatrix<T> adj = getAdjMatrix();
786+
const AdjacencyMatrix<T> adj = getAdjMatrix();
771787
std::function<void(const AdjacencyMatrix<T> &, const Node<T> &, std::vector<Node<T>> &)> explore;
772-
explore = [&explore](const AdjacencyMatrix<T> &adj, const Node<T> &node, std::vector<Node<T>> &visited) -> void {
788+
explore = [&explore](const AdjacencyMatrix<T> &adj, const Node<T> &node, std::vector<Node<T>> &visited) -> void
789+
{
773790
visited.push_back(node);
774791
if (adj.find(&node) != adj.end())
775792
{
@@ -787,20 +804,122 @@ namespace CXXGRAPH
787804
return visited;
788805
}
789806

807+
template <typename T>
808+
const bool Graph<T>::isCyclicDFS() const
809+
{
810+
if(!isDirectedGraph()){
811+
return false;
812+
}
813+
enum nodeStates : uint8_t
814+
{
815+
not_visited,
816+
in_stack,
817+
visited
818+
};
819+
auto nodeSet = this->getNodeSet();
820+
auto adjMatrix = this->getAdjMatrix();
821+
822+
/** State of the node.
823+
*
824+
* It is a vector of "nodeStates" which represents the state node is in.
825+
* It can take only 3 values: "not_visited", "in_stack", and "visited".
826+
*
827+
* Initially, all nodes are in "not_visited" state.
828+
*/
829+
std::map<unsigned long,nodeStates> state;
830+
for (auto node : nodeSet){
831+
state[node->getId()] = not_visited;
832+
}
833+
int stateCounter = 0;
834+
835+
// Start visiting each node.
836+
for (auto node : nodeSet)
837+
{
838+
// If a node is not visited, only then check for presence of cycle.
839+
// There is no need to check for presence of cycle for a visited
840+
// node as it has already been checked for presence of cycle.
841+
if (state[node->getId()] == not_visited)
842+
{
843+
// Check for cycle.
844+
std::function<bool(AdjacencyMatrix<T>&,std::map<unsigned long,nodeStates>&,const Node<T>*)> isCyclicDFSHelper;
845+
isCyclicDFSHelper = [this, &isCyclicDFSHelper](AdjacencyMatrix<T>& adjMatrix, std::map<unsigned long,nodeStates>& states, const Node<T>* node)
846+
{
847+
// Add node "in_stack" state.
848+
states[node->getId()] = in_stack;
849+
850+
// If the node has children, then recursively visit all children of the
851+
// node.
852+
auto const it = adjMatrix.find(node);
853+
if (it != adjMatrix.end())
854+
{
855+
for (auto child : it->second)
856+
{
857+
// If state of child node is "not_visited", evaluate that child
858+
// for presence of cycle.
859+
auto state_of_child = states.at((std::get<0>(child))->getId());
860+
if (state_of_child == not_visited)
861+
{
862+
if (isCyclicDFSHelper(adjMatrix, states, std::get<0>(child)))
863+
{
864+
return true;
865+
}
866+
}
867+
else if (state_of_child == in_stack)
868+
{
869+
// If child node was "in_stack", then that means that there
870+
// is a cycle in the graph. Return true for presence of the
871+
// cycle.
872+
return true;
873+
}
874+
}
875+
}
876+
877+
// Current node has been evaluated for the presence of cycle and had no
878+
// cycle. Mark current node as "visited".
879+
states[node->getId()] = visited;
880+
// Return that current node didn't result in any cycles.
881+
return false;
882+
};
883+
if (isCyclicDFSHelper(adjMatrix, state, node))
884+
{
885+
return true;
886+
}
887+
}
888+
}
889+
890+
// All nodes have been safely traversed, that means there is no cycle in
891+
// the graph. Return false.
892+
return false;
893+
}
894+
895+
template<typename T>
896+
const bool Graph<T>::isDirectedGraph() const
897+
{
898+
auto edgeSet = this->getEdgeSet();
899+
for( auto edge : edgeSet){
900+
if( !(edge->isDirected().has_value() && edge->isDirected().value())){
901+
//Found Undirected Edge
902+
return false;
903+
}
904+
}
905+
//No Undirected Edge
906+
return true;
907+
}
908+
790909
//ostream overload
791910
template <typename T>
792911
std::ostream &operator<<(std::ostream &os, const Node<T> &node)
793912
{
794913
os << "Node: {\n"
795914
<< " Id:\t" << node.id << "\n Data:\t" << node.data << "\n}";
796-
return os;
915+
return os;
797916
}
798917

799918
template <typename T>
800919
std::ostream &operator<<(std::ostream &os, const Edge<T> &edge)
801920
{
802921
os << "((Node: " << edge.nodePair.first->getId() << ")) ?----- |Edge: " << edge.id << "|-----? ((Node: " << edge.nodePair.second->getId() << "))";
803-
retrun os;
922+
return os;
804923
}
805924

806925
template <typename T>

test/CycleCheckTest.cpp

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#include "gtest/gtest.h"
2+
#include "Graph.hpp"
3+
4+
TEST(CycleCheckTest, test_1)
5+
{
6+
CXXGRAPH::Node<int> node1(1, 1);
7+
CXXGRAPH::Node<int> node2(2, 2);
8+
CXXGRAPH::Node<int> node3(3, 3);
9+
std::pair<const CXXGRAPH::Node<int> *, const CXXGRAPH::Node<int> *> pairNode(&node1, &node2);
10+
CXXGRAPH::DirectedWeightedEdge<int> edge1(1, pairNode,1);
11+
CXXGRAPH::DirectedWeightedEdge<int> edge2(2, node2, node3,1);
12+
CXXGRAPH::DirectedWeightedEdge<int> edge3(3, node1, node3,6);
13+
std::set<const CXXGRAPH::Edge<int> *> edgeSet;
14+
edgeSet.insert(&edge1);
15+
edgeSet.insert(&edge2);
16+
edgeSet.insert(&edge3);
17+
CXXGRAPH::Graph<int> graph(edgeSet);
18+
bool res= graph.isCyclicDFS();
19+
ASSERT_EQ(res,false);
20+
}
21+
22+
TEST(CycleCheckTest, test_2)
23+
{
24+
CXXGRAPH::Node<int> node1(1, 1);
25+
CXXGRAPH::Node<int> node2(2, 2);
26+
CXXGRAPH::Node<int> node3(3, 3);
27+
std::pair<const CXXGRAPH::Node<int> *, const CXXGRAPH::Node<int> *> pairNode(&node1, &node2);
28+
CXXGRAPH::DirectedWeightedEdge<int> edge1(1, pairNode,1);
29+
CXXGRAPH::DirectedWeightedEdge<int> edge2(2, node2, node3,1);
30+
std::set<const CXXGRAPH::Edge<int> *> edgeSet;
31+
edgeSet.insert(&edge1);
32+
edgeSet.insert(&edge2);
33+
CXXGRAPH::Graph<int> graph(edgeSet);
34+
bool res= graph.isCyclicDFS();
35+
ASSERT_EQ(res,false);
36+
}
37+
38+
TEST(CycleCheckTest, test_3)
39+
{
40+
CXXGRAPH::Node<int> node1(1, 1);
41+
CXXGRAPH::Node<int> node2(2, 2);
42+
CXXGRAPH::Node<int> node3(3, 3);
43+
std::pair<const CXXGRAPH::Node<int> *, const CXXGRAPH::Node<int> *> pairNode(&node1, &node2);
44+
CXXGRAPH::DirectedWeightedEdge<int> edge1(1, pairNode,1);
45+
CXXGRAPH::DirectedWeightedEdge<int> edge2(2, node2, node3,1);
46+
CXXGRAPH::UndirectedWeightedEdge<int> edge3(3, node1, node3,6);
47+
std::set<const CXXGRAPH::Edge<int> *> edgeSet;
48+
edgeSet.insert(&edge1);
49+
edgeSet.insert(&edge2);
50+
edgeSet.insert(&edge3);
51+
CXXGRAPH::Graph<int> graph(edgeSet);
52+
bool res= graph.isCyclicDFS();
53+
ASSERT_EQ(res,false);
54+
}
55+
56+
TEST(CycleCheckTest, test_4)
57+
{
58+
CXXGRAPH::Node<int> node1(1, 1);
59+
CXXGRAPH::Node<int> node2(2, 2);
60+
CXXGRAPH::Node<int> node3(3, 3);
61+
std::pair<const CXXGRAPH::Node<int> *, const CXXGRAPH::Node<int> *> pairNode(&node1, &node2);
62+
CXXGRAPH::DirectedWeightedEdge<int> edge1(1, pairNode,1);
63+
CXXGRAPH::DirectedWeightedEdge<int> edge2(2, node2, node3,1);
64+
CXXGRAPH::DirectedWeightedEdge<int> edge3(3, node3, node1,6);
65+
std::set<const CXXGRAPH::Edge<int> *> edgeSet;
66+
edgeSet.insert(&edge1);
67+
edgeSet.insert(&edge2);
68+
edgeSet.insert(&edge3);
69+
CXXGRAPH::Graph<int> graph(edgeSet);
70+
bool res= graph.isCyclicDFS();
71+
ASSERT_EQ(res,true);
72+
}
73+

0 commit comments

Comments
 (0)