From 8ba19d2dd02c9b9b9751d886cf85b20ac5ba8666 Mon Sep 17 00:00:00 2001 From: David Rosca <roscadav@fit.cvut.cz> Date: Sat, 7 Mar 2015 12:29:23 +0100 Subject: [PATCH] Graph algo: Add DFS + tests --- alib2algo/src/graph/traverse/Dfs.cpp | 71 ++++++++ alib2algo/src/graph/traverse/Dfs.h | 34 ++++ alib2algo/test-src/graph/traverse/DfsTest.cpp | 158 ++++++++++++++++++ alib2algo/test-src/graph/traverse/DfsTest.h | 20 +++ 4 files changed, 283 insertions(+) create mode 100644 alib2algo/src/graph/traverse/Dfs.cpp create mode 100644 alib2algo/src/graph/traverse/Dfs.h create mode 100644 alib2algo/test-src/graph/traverse/DfsTest.cpp create mode 100644 alib2algo/test-src/graph/traverse/DfsTest.h diff --git a/alib2algo/src/graph/traverse/Dfs.cpp b/alib2algo/src/graph/traverse/Dfs.cpp new file mode 100644 index 0000000000..30f00d3ce8 --- /dev/null +++ b/alib2algo/src/graph/traverse/Dfs.cpp @@ -0,0 +1,71 @@ +#include "Dfs.h" + +#include <stack> +#include <unordered_map> + +namespace graph +{ + +namespace traverse +{ + +struct Data +{ + Node start; + std::function<bool(const Node&)> func; +}; + +template <typename T> +static void dfs_impl(const T &graph, const Node &start, std::function<bool(const Node&)> func) +{ + std::unordered_map<Node, bool> visited; + std::stack<Node> s; + + s.push(start); + while (!s.empty()) { + Node n = s.top(); s.pop(); + if (visited.find(n) == visited.end()) { + if (!func(n)) { + return; + } + visited.insert({n, true}); + for (const Node &e : graph.neighbors(n)) { + s.push(e); + } + } + } +} + +void Dfs::dfs(const Graph &graph, const Node &start, std::function<bool(const Node&)> func) +{ + Data data = { start, func }; + graph.getData().Accept(static_cast<void*>(&data), DFS); +} + +void Dfs::dfs(const DirectedGraph &graph, const Node &start, std::function<bool(const Node&)> func) +{ + dfs_impl(graph, start, func); +} + +void Dfs::dfs(const UndirectedGraph &graph, const Node &start, std::function<bool(const Node&)> func) +{ + dfs_impl(graph, start, func); +} + +void Dfs::Visit(void *data, const DirectedGraph &graph) const +{ + Data d = *static_cast<Data*>(data); + dfs(graph, d.start, d.func); +} + +void Dfs::Visit(void *data, const UndirectedGraph &graph) const +{ + Data d = *static_cast<Data*>(data); + dfs(graph, d.start, d.func); +} + +const Dfs Dfs::DFS; + +} // namespace traverse + +} // namespace graph diff --git a/alib2algo/src/graph/traverse/Dfs.h b/alib2algo/src/graph/traverse/Dfs.h new file mode 100644 index 0000000000..85fa390ba3 --- /dev/null +++ b/alib2algo/src/graph/traverse/Dfs.h @@ -0,0 +1,34 @@ +#ifndef GRAPH_DFS_H_ +#define GRAPH_DFS_H_ + +#include <graph/Graph.h> +#include <graph/directed/DirectedGraph.h> +#include <graph/undirected/UndirectedGraph.h> + +namespace graph +{ + +namespace traverse +{ + +// func is called for each visited node, traversal stops when returning false +class Dfs : public graph::VisitableGraphBase::const_visitor_type +{ +public: + static void dfs(const Graph &graph, const Node &start, std::function<bool(const Node&)> func); + + static void dfs(const DirectedGraph &graph, const Node &start, std::function<bool(const Node&)> func); + static void dfs(const UndirectedGraph &graph, const Node &start, std::function<bool(const Node&)> func); + +private: + void Visit(void *data, const DirectedGraph &graph) const; + void Visit(void *data, const UndirectedGraph &graph) const; + + static const Dfs DFS; +}; + +} // namespace traverse + +} // namespace graph + +#endif // GRAPH_DFS_H_ diff --git a/alib2algo/test-src/graph/traverse/DfsTest.cpp b/alib2algo/test-src/graph/traverse/DfsTest.cpp new file mode 100644 index 0000000000..f4c8071e83 --- /dev/null +++ b/alib2algo/test-src/graph/traverse/DfsTest.cpp @@ -0,0 +1,158 @@ +#include "DfsTest.h" + +#include "graph/traverse/Dfs.h" + +CPPUNIT_TEST_SUITE_REGISTRATION(GraphDfsTest); + +void GraphDfsTest::testTraverseAll() +{ + // Common + int counter; + graph::Node n1("n1"); + graph::Node n2("n2"); + graph::Node n3("n3"); + graph::Node n4("n4"); + graph::Node n5("n5"); + graph::Node n6("n6"); + + // Directed + graph::DirectedGraph dg; + dg.addEdge(graph::DirectedEdge(n1, n2)); + dg.addEdge(graph::DirectedEdge(n1, n2, "multi-edge")); + dg.addEdge(graph::DirectedEdge(n2, n3)); + dg.addEdge(graph::DirectedEdge(n3, n4)); + dg.addEdge(graph::DirectedEdge(n3, n6)); + dg.addEdge(graph::DirectedEdge(n4, n1)); + dg.addEdge(graph::DirectedEdge(n4, n5)); + + counter = 0; + graph::traverse::Dfs::dfs(dg, n1, [&counter](const graph::Node &) { + counter++; + return true; + }); + + CPPUNIT_ASSERT_EQUAL(6, counter); + + // Undirected + graph::UndirectedGraph ug; + ug.addEdge(graph::UndirectedEdge(n1, n2)); + ug.addEdge(graph::UndirectedEdge(n1, n2, "multi-edge")); + ug.addEdge(graph::UndirectedEdge(n2, n3)); + ug.addEdge(graph::UndirectedEdge(n3, n4)); + ug.addEdge(graph::UndirectedEdge(n3, n6)); + ug.addEdge(graph::UndirectedEdge(n4, n1)); + ug.addEdge(graph::UndirectedEdge(n4, n5)); + + counter = 0; + graph::traverse::Dfs::dfs(ug, n1, [&counter](const graph::Node &) { + counter++; + return true; + }); + + CPPUNIT_ASSERT_EQUAL(6, counter); +} + +void GraphDfsTest::testEarlyReturn() +{ + // Common + int counter; + graph::Node n1("n1"); + graph::Node n2("n2"); + graph::Node n3("n3"); + graph::Node n4("n4"); + + // Directed + graph::DirectedGraph dg; + dg.addEdge(graph::DirectedEdge(n1, n2)); + dg.addEdge(graph::DirectedEdge(n1, n2, "multi-edge")); + dg.addEdge(graph::DirectedEdge(n2, n3)); + dg.addEdge(graph::DirectedEdge(n3, n4)); + + counter = 0; + graph::traverse::Dfs::dfs(dg, n1, [&counter](const graph::Node &) { + counter++; + if (counter == 2) { + return false; + } + return true; + }); + + CPPUNIT_ASSERT_EQUAL(2, counter); + + // Undirected + graph::UndirectedGraph ug; + ug.addEdge(graph::UndirectedEdge(n1, n2)); + ug.addEdge(graph::UndirectedEdge(n1, n2, "multi-edge")); + ug.addEdge(graph::UndirectedEdge(n2, n3)); + ug.addEdge(graph::UndirectedEdge(n3, n4)); + + counter = 0; + graph::traverse::Dfs::dfs(ug, n1, [&counter](const graph::Node &) { + counter++; + if (counter == 2) { + return false; + } + return true; + }); + + CPPUNIT_ASSERT_EQUAL(2, counter); +} + +void GraphDfsTest::testDisconnectedGraph() +{ + // Common + int counter; + graph::Node n1("n1"); + graph::Node n2("n2"); + graph::Node n3("n3"); + graph::Node n4("n4"); + graph::Node n5("n5"); + + // Directed + graph::DirectedGraph dg; + dg.addNode(n5); + dg.addEdge(graph::DirectedEdge(n1, n2)); + dg.addEdge(graph::DirectedEdge(n1, n2, "multi-edge")); + dg.addEdge(graph::DirectedEdge(n2, n3)); + dg.addEdge(graph::DirectedEdge(n3, n4)); + + counter = 0; + graph::traverse::Dfs::dfs(dg, n1, [&counter](const graph::Node &) { + counter++; + return true; + }); + + CPPUNIT_ASSERT_EQUAL(4, counter); + + counter = 0; + graph::traverse::Dfs::dfs(dg, n5, [&counter](const graph::Node &) { + counter++; + return true; + }); + + CPPUNIT_ASSERT_EQUAL(1, counter); + + // Undirected + graph::UndirectedGraph ug; + ug.addNode(n5); + ug.addEdge(graph::UndirectedEdge(n1, n2)); + ug.addEdge(graph::UndirectedEdge(n1, n2, "multi-edge")); + ug.addEdge(graph::UndirectedEdge(n2, n3)); + ug.addEdge(graph::UndirectedEdge(n3, n4)); + + counter = 0; + graph::traverse::Dfs::dfs(ug, n1, [&counter](const graph::Node &) { + counter++; + return true; + }); + + CPPUNIT_ASSERT_EQUAL(4, counter); + + counter = 0; + graph::traverse::Dfs::dfs(ug, n5, [&counter](const graph::Node &) { + counter++; + return true; + }); + + CPPUNIT_ASSERT_EQUAL(1, counter); +} diff --git a/alib2algo/test-src/graph/traverse/DfsTest.h b/alib2algo/test-src/graph/traverse/DfsTest.h new file mode 100644 index 0000000000..c3bf1350e4 --- /dev/null +++ b/alib2algo/test-src/graph/traverse/DfsTest.h @@ -0,0 +1,20 @@ +#ifndef DFS_TEST_H_ +#define DFS_TEST_H_ + +#include <cppunit/extensions/HelperMacros.h> + +class GraphDfsTest : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE(GraphDfsTest); + CPPUNIT_TEST(testTraverseAll); + CPPUNIT_TEST(testEarlyReturn); + CPPUNIT_TEST(testDisconnectedGraph); + CPPUNIT_TEST_SUITE_END(); + +public: + void testTraverseAll(); + void testEarlyReturn(); + void testDisconnectedGraph(); +}; + +#endif // DFS_TEST_H_ -- GitLab