Skip to content

Commit

Permalink
Resolves #106 (#138)
Browse files Browse the repository at this point in the history
* eulerian path using hierholzers algorithm

* eulerian path test

* update cmake
  • Loading branch information
sidml authored Oct 20, 2021
1 parent eb282bd commit d0c8c14
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 0 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ add_executable(test_exe test/main.cpp
test/DialTest.cpp
test/GraphSlicingTest.cpp
test/UnionFindTest.cpp
test/EulerPathTest.cpp
)
target_include_directories(test_exe PUBLIC
"${PROJECT_SOURCE_DIR}/include"
Expand Down
39 changes: 39 additions & 0 deletions include/Graph/Graph.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,13 @@ namespace CXXGRAPH
*/
virtual void setUnion(std::vector<Subset>*, const unsigned long set1, const unsigned long elem2) const;
/**
* @brief This function finds the eulerian path of a directed graph using hierholzers algorithm
*
* @return a vector containing nodes in eulerian path
* Note: No Thread Safe
*/
virtual std::vector<Node<T>> eulerianPath() const;
/**
* @brief Function runs the dijkstra algorithm for some source node and
* target node in the graph and returns the shortest distance of target
* from the source.
Expand Down Expand Up @@ -962,6 +969,38 @@ namespace CXXGRAPH
}
}

template <typename T>
std::vector<Node<T>> Graph<T>::eulerianPath() const
{
const auto nodeSet = Graph<T>::getNodeSet();
auto adj = Graph<T>::getAdjMatrix();
std::vector<Node<T>> eulerPath;
std::vector<const Node<T> *> currentPath;
auto currentNode = nodeSet.front();
currentPath.push_back(currentNode);
while (currentPath.size() > 0)
{
auto &edges = adj.at(currentNode);
// we keep removing the edges that
// have been traversed from the adjacency list
if (edges.size())
{
auto firstEdge = edges.back().second;
auto nextNodeId = firstEdge->getNodePair().second;
currentPath.push_back(nextNodeId);
currentNode = nextNodeId;
edges.pop_back();
}
else
{
eulerPath.push_back(*currentNode);
currentNode = currentPath.back();
currentPath.pop_back();
}
}
return eulerPath;
}

template <typename T>
const AdjacencyMatrix<T> Graph<T>::getAdjMatrix() const
{
Expand Down
114 changes: 114 additions & 0 deletions test/EulerPathTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#include "gtest/gtest.h"
#include "CXXGraph.hpp"

// example taken from
// https://www.geeksforgeeks.org/hierholzers-algorithm-directed-graph/
// different eulerian paths (i.e different node visit order)
// are possible for the same graph
// so we check if all the nodes in the graph are atleast traversed
// once in the eulerian path
TEST(EulerPathTest, test_1)
{
CXXGRAPH::Node<int> node0(0, 0);
CXXGRAPH::Node<int> node1(1, 1);
CXXGRAPH::Node<int> node2(2, 2);
// CXXGRAPH::Node<int> node3(3, 3);

CXXGRAPH::DirectedWeightedEdge<int> edge1(1, node0, node1, 4);
CXXGRAPH::DirectedWeightedEdge<int> edge2(2, node1, node2, 8);
CXXGRAPH::DirectedWeightedEdge<int> edge3(3, node2, node0, 11);
std::list<const CXXGRAPH::Edge<int> *> edgeSet;
edgeSet.push_back(&edge1);
edgeSet.push_back(&edge2);
edgeSet.push_back(&edge3);

CXXGRAPH::Graph<int> graph(edgeSet);
auto res = graph.eulerianPath();
auto nodeSet = graph.getNodeSet();
for (auto node : nodeSet)
{
auto check = std::find_if(res.begin(), res.end(),
[node](auto it)
{ return (node->getId() == it.getId()); }) == res.end();

ASSERT_FALSE(check);
}
}

TEST(EulerPathTest, test_2)
{
CXXGRAPH::Node<int> node0(0, 0);
CXXGRAPH::Node<int> node1(1, 1);
CXXGRAPH::Node<int> node2(2, 2);
CXXGRAPH::Node<int> node3(3, 3);
CXXGRAPH::Node<int> node4(4, 4);

CXXGRAPH::DirectedWeightedEdge<int> edge1(1, node0, node1, 4);
CXXGRAPH::DirectedWeightedEdge<int> edge2(2, node1, node2, 8);
CXXGRAPH::DirectedWeightedEdge<int> edge3(3, node2, node0, 11);
CXXGRAPH::DirectedWeightedEdge<int> edge4(4, node1, node3, 11);
CXXGRAPH::DirectedWeightedEdge<int> edge5(5, node3, node4, 11);
CXXGRAPH::DirectedWeightedEdge<int> edge6(6, node4, node1, 11);

std::list<const CXXGRAPH::Edge<int> *> edgeSet;
edgeSet.push_back(&edge1);
edgeSet.push_back(&edge2);
edgeSet.push_back(&edge3);
edgeSet.push_back(&edge4);
edgeSet.push_back(&edge5);
edgeSet.push_back(&edge6);

CXXGRAPH::Graph<int> graph(edgeSet);
auto res = graph.eulerianPath();
auto nodeSet = graph.getNodeSet();
for (auto node : nodeSet)
{
auto check = std::find_if(res.begin(), res.end(),
[node](auto it)
{ return (node->getId() == it.getId()); }) == res.end();

ASSERT_FALSE(check);
}
}

TEST(EulerPathTest, test_3)
{
CXXGRAPH::Node<int> node0(0, 0);
CXXGRAPH::Node<int> node1(1, 1);
CXXGRAPH::Node<int> node2(2, 2);
CXXGRAPH::Node<int> node3(3, 3);
CXXGRAPH::Node<int> node4(4, 4);
CXXGRAPH::Node<int> node5(5, 5);
CXXGRAPH::Node<int> node6(6, 6);

CXXGRAPH::DirectedEdge<int> edge1(1, node0, node1);
CXXGRAPH::DirectedEdge<int> edge2(2, node0, node6);
CXXGRAPH::DirectedEdge<int> edge3(3, node1, node2);
CXXGRAPH::DirectedEdge<int> edge4(4, node2, node3);
CXXGRAPH::DirectedEdge<int> edge5(5, node3, node4);
CXXGRAPH::DirectedEdge<int> edge6(6, node4, node5);
CXXGRAPH::DirectedEdge<int> edge7(6, node5, node0);
CXXGRAPH::DirectedEdge<int> edge8(6, node6, node4);

std::list<const CXXGRAPH::Edge<int> *> edgeSet;
edgeSet.push_back(&edge1);
edgeSet.push_back(&edge2);
edgeSet.push_back(&edge3);
edgeSet.push_back(&edge4);
edgeSet.push_back(&edge5);
edgeSet.push_back(&edge6);
edgeSet.push_back(&edge7);
edgeSet.push_back(&edge8);

CXXGRAPH::Graph<int> graph(edgeSet);
auto res = graph.eulerianPath();
auto nodeSet = graph.getNodeSet();
for (auto node : nodeSet)
{
auto check = std::find_if(res.begin(), res.end(),
[node](auto it)
{ return (node->getId() == it.getId()); }) == res.end();

ASSERT_FALSE(check);
}
}

1 comment on commit d0c8c14

@ZigRazor
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 2.

Benchmark suite Current: d0c8c14 Previous: eb282bd Ratio
RemoveEdgeX_MT_TS/4096/threads:2 697361.4229411851 ns/iter 166912.13847475787 ns/iter 4.18

This comment was automatically generated by workflow using github-action-benchmark.

CC: @ZigRazor

Please sign in to comment.