diff --git a/algorithm/tarjan.h b/algorithm/tarjan.h index 3a72da6..a78d58a 100644 --- a/algorithm/tarjan.h +++ b/algorithm/tarjan.h @@ -4,71 +4,80 @@ #include "graph/graph.h" using namespace graph; -#include +#include #include #include -#include -#include +#include -namespace algorithm { +namespace algo { template class Tarjan { public: Tarjan(Graph G) : G(G) {} - void findSCC(); + // + std::vector> run(); - void strongConnect(Vertex& v); - - std::uint16_t index = 0; - std::stack> S; + // + void strongConnect(const T& v); private: + // Necessary info about vertices when running Tarjan's algorithm + struct Payload { + std::int16_t index = -1; + std::int16_t lowlink = -1; + bool onStack = false; + }; + Graph G; + std::stack S; + std::int16_t index = 0; + std::map vp; + std::vector> SCCs; }; template -void Tarjan::strongConnect(Vertex& v) { - v.index, v.lowlink = index++; - v.onStack = true; +inline void Tarjan::strongConnect(const T& v) { + vp[v].index = vp[v].lowlink = index++; + vp[v].onStack = true; S.push(v); - - for (auto& w : v.edges) { - if (w.index == -1) { - // successor w has not yet been visited, recurse on it + + for (const auto& w : G.adjMatrix[v]) { + if (vp[w].index == -1) { strongConnect(w); - v.lowlink = std::min(v.lowlink, w.lowlink); - } else if (w.onStack) { - // successor w is in stack S and hence in the current SCC - // if w is not on stack, then (v, w) is an edge pointing to an SCC - // already found and must be ignored - v.lowlink = std::min(v.lowlink, w.index); + vp[v].lowlink = std::min(vp[v].lowlink, vp[w].lowlink); + } else if (vp[w].onStack) { + vp[v].lowlink = std::min(vp[v].lowlink, vp[w].index); } } - // if v is a root node, pop the stack and generate an SCC - if (v.lowlink = v.index) { - // start a new SCC - Vertex w; + // If v is a root node, pop the stack and generate an SCC + if (vp[v].lowlink == vp[v].index) { + std::vector SCC; + bool finished = false; do { - w = S.top(); + const auto& w = S.top(); S.pop(); - // add w to current SCC - - } while (w != v); + vp[w].onStack = false; + SCC.push_back(w); + finished = vp[w].index == vp[v].index; + } while (!finished); + SCCs.push_back(SCC); } } template -void Tarjan::findSCC() { +inline std::vector> Tarjan::run() { - for (auto& vertex : G.vertices) { - if (vertex.index == -1) { - strongConnect(vertex); + for (const auto& v : G.vertices) { + if (vp[v].index == -1) { + strongConnect(v); } } + + return SCCs; } -} // namespace algorithm +} // namespace algo #endif \ No newline at end of file diff --git a/graph/graph.h b/graph/graph.h index a661953..cae2e89 100644 --- a/graph/graph.h +++ b/graph/graph.h @@ -1,12 +1,11 @@ #ifndef GRAPH_H_ #define GRAPH_H_ -#include "vertex.h" - #include #include -#include #include +#include +#include namespace graph { @@ -16,50 +15,44 @@ public: Graph() = default; // Add vertex v - void insert(Vertex& v); + void insert(const T& v); // Add edge between v and u - void insert(Vertex v, Vertex u); + void insert(const T& v, const T& u); // Delete vertex v - void erase(Vertex& v); + void erase(T& v); // Delete edge between v and u - void erase(Vertex& v, Vertex& u); - - // Return true if v and u are connected - bool connected(const Vertex& v, const Vertex& u) const; + void erase(T& v, T& u); // Adjacency matrix representation - std::vector> vertices; + std::set vertices; + std::map> adjMatrix; }; template -inline void Graph::insert(Vertex& v) { - vertices.push_back(v); +inline void Graph::insert(const T& v) { + vertices.insert(v); } template -inline void Graph::insert(Vertex v, Vertex u) { - v.edges.push_back(u); - vertices.push_back(v); +inline void Graph::insert(const T& v, const T& u) { + vertices.insert(v); + vertices.insert(u); + adjMatrix[v].push_back(u); } template -inline void Graph::erase(Vertex& v) { +inline void Graph::erase(T& v) { // } template -inline void Graph::erase(Vertex& v, Vertex& u) { +inline void Graph::erase(T& v, T& u) { // } -template -inline bool Graph::connected(const Vertex& v, const Vertex& u) const { - return false; -} - } // namespace graph #endif \ No newline at end of file diff --git a/graph/vertex.h b/graph/vertex.h deleted file mode 100644 index 9d447ee..0000000 --- a/graph/vertex.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef VERTEX_H_ -#define VERTEX_H_ - -#include -#include -#include -#include - -namespace graph { - -template -class Vertex { -public: - Vertex() = default; - Vertex(T v) : v(v) {} - - T v; - std::uint16_t index = -1; - std::uint16_t lowlink = -1; - bool onStack = false; - std::vector> edges; - - auto operator<=>(const Vertex&) const = default; - - template - friend inline std::ostream& operator<<(std::ostream& os, const Vertex& v); -}; - -template -inline std::ostream& operator<<(std::ostream& os, const Vertex& vout) { - return os << vout.v; -} - -} // namespace graph - -#endif \ No newline at end of file diff --git a/test/algorithm_test.cc b/test/algorithm_test.cc new file mode 100644 index 0000000..e36d5d1 --- /dev/null +++ b/test/algorithm_test.cc @@ -0,0 +1,73 @@ +#include + +#include "graph/graph.h" +using namespace graph; +#include "algorithm/tarjan.h" + +TEST_SUITE("Algorithms") { + + TEST_CASE("Tarjan T_1") { + + Graph G; + G.insert(1, 2); + G.insert(2, 4); + G.insert(2, 3); + G.insert(3, 5); + G.insert(4, 1); + G.insert(5, 7); + G.insert(5, 9); + G.insert(6, 8); + G.insert(7, 3); + G.insert(8, 9); + G.insert(9, 6); + + REQUIRE_EQ(G.vertices.size(), 9); + + algo::Tarjan tarjan(G); + auto result = tarjan.run(); + + std::sort(result.begin(), result.end()); + std::for_each(result.begin(), result.end(), [&](std::vector& row) { + std::sort(row.begin(), row.end()); + }); + + std::vector> expected{ + {1, 2, 4}, + {3, 5, 7}, + {6, 8, 9} + }; + + CHECK_EQ(expected, result); + } + + TEST_CASE("Tarjan T_2") { + + Graph G; + G.insert(1, 4); + G.insert(1, 2); + G.insert(2, 5); + G.insert(3, 1); + G.insert(4, 3); + G.insert(4, 6); + G.insert(5, 7); + G.insert(6, 3); + G.insert(7, 2); + + REQUIRE_EQ(G.vertices.size(), 7); + + algo::Tarjan tarjan(G); + auto result = tarjan.run(); + + std::sort(result.begin(), result.end()); + std::for_each(result.begin(), result.end(), [&](std::vector& row) { + std::sort(row.begin(), row.end()); + }); + + std::vector> expected{ + {1, 3, 4, 6}, + {2, 5, 7} + }; + + CHECK_EQ(expected, result); + } +} \ No newline at end of file diff --git a/test/graph_test.cc b/test/graph_test.cc index 7720b65..0244e0a 100644 --- a/test/graph_test.cc +++ b/test/graph_test.cc @@ -2,33 +2,22 @@ #include "graph/graph.h" using namespace graph; -#include "algorithm/tarjan.h" -#include -#include +TEST_SUITE("Graph test.") { -TEST_SUITE("Testing Graph.") { - - TEST_CASE("Insert vertices/edges.") { + TEST_CASE("Insertion.") { Graph G; + //G.insert(1); + //G.insert(1, 2); + //G.insert(1, 4); + //G.insert(1, 6); + //G.insert(2, 4); + //G.insert(2, 5); + //G.insert(3, 4); + //G.insert(3, 6); + //G.insert(4, 6); + //G.insert(5, 6); - G.insert(1, 2); - G.insert(1, 4); - G.insert(1, 6); - G.insert(2, 4); - G.insert(2, 5); - G.insert(3, 4); - G.insert(3, 6); - G.insert(4, 6); - G.insert(5, 6); - Vertex v1(1); - Vertex v2(3); - Vertex v3(5); - algorithm::Tarjan tarjan(G); - tarjan.findSCC(); - - // CHECK_EQ(G.connected(1, 5), true); - } } \ No newline at end of file