Skip to content

Commit b5613e1

Browse files
authored
Introduced Cycle Check with BFS for directed Graph
1 parent 48a0c0e commit b5613e1

File tree

4 files changed

+164
-11
lines changed

4 files changed

+164
-11
lines changed

CMakeLists.txt

Lines changed: 1 addition & 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.5)
4+
project(CXXGraph VERSION 0.0.6)
55

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

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ Applications of Breadth First Search are :
4343

4444
And there are many more...
4545

46-
####Cycle Detection
46+
#### Cycle Detection
4747
[Cycle (graph theory)](https://en.wikipedia.org/wiki/Cycle_(graph_theory))
4848
The existence of a cycle in directed and undirected graphs can be determined by whether depth-first search (DFS) finds an edge that points to an ancestor of the current vertex (it contains a back edge). All the back edges which DFS skips over are part of cycles. In an undirected graph, the edge to the parent of a node should not be counted as a back edge, but finding any other already visited vertex will indicate a back edge. In the case of undirected graphs, only O(n) time is required to find a cycle in an n-vertex graph, since at most n − 1 edges can be tree edges.
4949

include/Graph.hpp

Lines changed: 92 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,16 @@ namespace CXXGRAPH
517517
* @return true if a cycle is detected, else false. ( false is returned also if the graph in indirected)
518518
*/
519519
const bool isCyclicDirectedGraphDFS() const;
520+
521+
/**
522+
* \brief
523+
* This function uses BFS to check for cycle in the graph.
524+
* Pay Attention, this function work only with directed Graph
525+
*
526+
* @return true if a cycle is detected, else false. ( false is returned also if the graph in indirected)
527+
*/
528+
const bool isCyclicDirectedGraphBFS() const;
529+
520530
/**
521531
* \brief
522532
* This function checks if a graph is directed
@@ -807,7 +817,8 @@ namespace CXXGRAPH
807817
template <typename T>
808818
const bool Graph<T>::isCyclicDirectedGraphDFS() const
809819
{
810-
if(!isDirectedGraph()){
820+
if (!isDirectedGraph())
821+
{
811822
return false;
812823
}
813824
enum nodeStates : uint8_t
@@ -826,8 +837,9 @@ namespace CXXGRAPH
826837
*
827838
* Initially, all nodes are in "not_visited" state.
828839
*/
829-
std::map<unsigned long,nodeStates> state;
830-
for (auto node : nodeSet){
840+
std::map<unsigned long, nodeStates> state;
841+
for (auto node : nodeSet)
842+
{
831843
state[node->getId()] = not_visited;
832844
}
833845
int stateCounter = 0;
@@ -841,8 +853,8 @@ namespace CXXGRAPH
841853
if (state[node->getId()] == not_visited)
842854
{
843855
// 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)
856+
std::function<bool(AdjacencyMatrix<T> &, std::map<unsigned long, nodeStates> &, const Node<T> *)> isCyclicDFSHelper;
857+
isCyclicDFSHelper = [this, &isCyclicDFSHelper](AdjacencyMatrix<T> &adjMatrix, std::map<unsigned long, nodeStates> &states, const Node<T> *node)
846858
{
847859
// Add node "in_stack" state.
848860
states[node->getId()] = in_stack;
@@ -891,13 +903,84 @@ namespace CXXGRAPH
891903
// the graph. Return false.
892904
return false;
893905
}
894-
895-
template<typename T>
906+
907+
template <typename T>
908+
const bool Graph<T>::isCyclicDirectedGraphBFS() const
909+
{
910+
if (!isDirectedGraph())
911+
{
912+
return false;
913+
}
914+
auto adjMatrix = this->getAdjMatrix();
915+
auto nodeSet = this->getNodeSet();
916+
917+
std::map<unsigned long, unsigned int> indegree;
918+
for (auto node : nodeSet)
919+
{
920+
indegree[node->getId()] = 0;
921+
}
922+
// Calculate the indegree i.e. the number of incident edges to the node.
923+
for (auto const &list : adjMatrix)
924+
{
925+
auto children = list.second;
926+
for (auto const &child : children)
927+
{
928+
indegree[std::get<0>(child)->getId()]++;
929+
}
930+
}
931+
932+
std::queue<const Node<T> *> can_be_solved;
933+
for (auto node : nodeSet)
934+
{
935+
// If a node doesn't have any input edges, then that node will
936+
// definately not result in a cycle and can be visited safely.
937+
if (!indegree[node->getId()])
938+
{
939+
can_be_solved.emplace(&(*node));
940+
}
941+
}
942+
943+
// Vertices that need to be traversed.
944+
auto remain = this->getNodeSet().size();
945+
// While there are safe nodes that we can visit.
946+
while (!can_be_solved.empty())
947+
{
948+
auto solved = can_be_solved.front();
949+
// Visit the node.
950+
can_be_solved.pop();
951+
// Decrease number of nodes that need to be traversed.
952+
remain--;
953+
954+
// Visit all the children of the visited node.
955+
auto it = adjMatrix.find(solved);
956+
if (it != adjMatrix.end())
957+
{
958+
for (auto child : it->second)
959+
{
960+
// Check if we can visited the node safely.
961+
if (--indegree[std::get<0>(child)->getId()] == 0)
962+
{
963+
// if node can be visited safely, then add that node to
964+
// the visit queue.
965+
can_be_solved.emplace(std::get<0>(child));
966+
}
967+
}
968+
}
969+
}
970+
971+
// If there are still nodes that we can't visit, then it means that
972+
// there is a cycle and return true, else return false.
973+
return !(remain == 0);
974+
}
975+
976+
template <typename T>
896977
const bool Graph<T>::isDirectedGraph() const
897978
{
898979
auto edgeSet = this->getEdgeSet();
899-
for( auto edge : edgeSet){
900-
if( !(edge->isDirected().has_value() && edge->isDirected().value())){
980+
for (auto edge : edgeSet)
981+
{
982+
if (!(edge->isDirected().has_value() && edge->isDirected().value()))
983+
{
901984
//Found Undirected Edge
902985
return false;
903986
}

test/CycleCheckTest.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,73 @@ TEST(CycleCheckTest, test_4)
7171
ASSERT_EQ(res,true);
7272
}
7373

74+
TEST(CycleCheckTest, test_5)
75+
{
76+
CXXGRAPH::Node<int> node1(1, 1);
77+
CXXGRAPH::Node<int> node2(2, 2);
78+
CXXGRAPH::Node<int> node3(3, 3);
79+
std::pair<const CXXGRAPH::Node<int> *, const CXXGRAPH::Node<int> *> pairNode(&node1, &node2);
80+
CXXGRAPH::DirectedWeightedEdge<int> edge1(1, pairNode,1);
81+
CXXGRAPH::DirectedWeightedEdge<int> edge2(2, node2, node3,1);
82+
CXXGRAPH::DirectedWeightedEdge<int> edge3(3, node1, node3,6);
83+
std::set<const CXXGRAPH::Edge<int> *> edgeSet;
84+
edgeSet.insert(&edge1);
85+
edgeSet.insert(&edge2);
86+
edgeSet.insert(&edge3);
87+
CXXGRAPH::Graph<int> graph(edgeSet);
88+
bool res= graph.isCyclicDirectedGraphBFS();
89+
ASSERT_EQ(res,false);
90+
}
91+
92+
TEST(CycleCheckTest, test_6)
93+
{
94+
CXXGRAPH::Node<int> node1(1, 1);
95+
CXXGRAPH::Node<int> node2(2, 2);
96+
CXXGRAPH::Node<int> node3(3, 3);
97+
std::pair<const CXXGRAPH::Node<int> *, const CXXGRAPH::Node<int> *> pairNode(&node1, &node2);
98+
CXXGRAPH::DirectedWeightedEdge<int> edge1(1, pairNode,1);
99+
CXXGRAPH::DirectedWeightedEdge<int> edge2(2, node2, node3,1);
100+
std::set<const CXXGRAPH::Edge<int> *> edgeSet;
101+
edgeSet.insert(&edge1);
102+
edgeSet.insert(&edge2);
103+
CXXGRAPH::Graph<int> graph(edgeSet);
104+
bool res= graph.isCyclicDirectedGraphBFS();
105+
ASSERT_EQ(res,false);
106+
}
107+
108+
TEST(CycleCheckTest, test_7)
109+
{
110+
CXXGRAPH::Node<int> node1(1, 1);
111+
CXXGRAPH::Node<int> node2(2, 2);
112+
CXXGRAPH::Node<int> node3(3, 3);
113+
std::pair<const CXXGRAPH::Node<int> *, const CXXGRAPH::Node<int> *> pairNode(&node1, &node2);
114+
CXXGRAPH::DirectedWeightedEdge<int> edge1(1, pairNode,1);
115+
CXXGRAPH::DirectedWeightedEdge<int> edge2(2, node2, node3,1);
116+
CXXGRAPH::UndirectedWeightedEdge<int> edge3(3, node1, node3,6);
117+
std::set<const CXXGRAPH::Edge<int> *> edgeSet;
118+
edgeSet.insert(&edge1);
119+
edgeSet.insert(&edge2);
120+
edgeSet.insert(&edge3);
121+
CXXGRAPH::Graph<int> graph(edgeSet);
122+
bool res= graph.isCyclicDirectedGraphBFS();
123+
ASSERT_EQ(res,false);
124+
}
125+
126+
TEST(CycleCheckTest, test_8)
127+
{
128+
CXXGRAPH::Node<int> node1(1, 1);
129+
CXXGRAPH::Node<int> node2(2, 2);
130+
CXXGRAPH::Node<int> node3(3, 3);
131+
std::pair<const CXXGRAPH::Node<int> *, const CXXGRAPH::Node<int> *> pairNode(&node1, &node2);
132+
CXXGRAPH::DirectedWeightedEdge<int> edge1(1, pairNode,1);
133+
CXXGRAPH::DirectedWeightedEdge<int> edge2(2, node2, node3,1);
134+
CXXGRAPH::DirectedWeightedEdge<int> edge3(3, node3, node1,6);
135+
std::set<const CXXGRAPH::Edge<int> *> edgeSet;
136+
edgeSet.insert(&edge1);
137+
edgeSet.insert(&edge2);
138+
edgeSet.insert(&edge3);
139+
CXXGRAPH::Graph<int> graph(edgeSet);
140+
bool res= graph.isCyclicDirectedGraphBFS();
141+
ASSERT_EQ(res,true);
142+
}
143+

0 commit comments

Comments
 (0)