From 5c1b13b400966a3a313c73641e73e57fc701ebd0 Mon Sep 17 00:00:00 2001 From: stefiosif Date: Fri, 9 Sep 2022 18:12:38 +0300 Subject: [PATCH] Add default param constructors, create bool query for bfs and tests --- algorithm/breadth_first_search.h | 68 ++++++++---- algorithm/tarjan.h | 78 +++++++------- test/algorithm_test.cc | 172 +++++++++++++------------------ 3 files changed, 161 insertions(+), 157 deletions(-) diff --git a/algorithm/breadth_first_search.h b/algorithm/breadth_first_search.h index 08b905b..84168d1 100644 --- a/algorithm/breadth_first_search.h +++ b/algorithm/breadth_first_search.h @@ -1,8 +1,8 @@ #ifndef BREADTH_FIRST_SEARCH_H_ #define BREADTH_FIRST_SEARCH_H_ -#include "graph/digraph.h" - +#include +#include #include using namespace graph; @@ -14,49 +14,73 @@ class BreadthFirstSearch { public: BreadthFirstSearch() = default; - BreadthFirstSearch(Digraph G) : G(G) {} + BreadthFirstSearch(std::map> adjMatrix) + : adjMatrix(adjMatrix) {} + // Traverse whole graph using the BFS search, and save the tree graph + // which is created when visiting new vertices (Breadth First Tree) std::map> execute(const T& root); - // Initialize LU table that show which vertices have been traversed - std::map initExplore(); -private: - Graph G; -}; + // Search if target vertex exists in graph + bool query(const T& root, const T& target); -template -std::map BreadthFirstSearch::initExplore() { - std::map graphExplore; - for (const auto& v : G.adjMatrix) { - graphExplore[v.first] = false; - } - return graphExplore; -} + void setGraph(std::map> adjMatrix); +private: + // Represents the graph on which the algorithm will be executed + std::map> adjMatrix; +}; template std::map> BreadthFirstSearch::execute(const T& root) { std::map> tree; - std::map graphExplore = initExplore(); + std::map visited; std::queue Q; Q.push(root); - graphExplore[root] = true; + visited[root] = true; while (!Q.empty()) { const auto v = Q.front(); Q.pop(); - for (const auto& u : G.adjMatrix[v]) { - if (!graphExplore[u]) { - graphExplore[u] = true; + for (const auto& u : adjMatrix[v]) { + if (!visited[u]) { + visited[u] = true; tree[v].insert(u); Q.push(u); } } } - return tree; } +template +bool BreadthFirstSearch::query(const T& root, const T& target) { + std::map visited; + std::queue Q; + Q.push(root); + visited[root] = true; + + while (!Q.empty()) { + const auto v = Q.front(); + Q.pop(); + + if (v == target) return true; + + for (const auto& u : adjMatrix[v]) { + if (!visited[u]) { + visited[u] = true; + Q.push(u); + } + } + } + return false; +} + +template +void BreadthFirstSearch::setGraph(std::map> adjMatrix) { + this->adjMatrix = adjMatrix; +} + } // namespace algo #endif \ No newline at end of file diff --git a/algorithm/tarjan.h b/algorithm/tarjan.h index 1817bba..17bcbb8 100644 --- a/algorithm/tarjan.h +++ b/algorithm/tarjan.h @@ -5,6 +5,7 @@ #include #include +#include using namespace graph; @@ -13,68 +14,75 @@ namespace algo { template class Tarjan { public: - Tarjan(Digraph G) : G(G) {} + Tarjan() = default; - std::vector> execute(); + Tarjan(std::map> adjMatrix) : adjMatrix(adjMatrix) {} - void strongConnect(const T& v); + auto execute(); + + void strongConnect(const T& u); + + void setGraph(std::map> adjMatrix); 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; - }; - - Digraph G; + std::map> adjMatrix; std::stack S; std::int16_t index = 0; - std::map p; std::vector> SCCs; + + struct Vertex { + int index = -1; + int lowlink = -1; + bool onStack = false; + }; + std::map vmap; }; template -void Tarjan::strongConnect(const T& v) { - p[v].index = p[v].lowlink = index++; - p[v].onStack = true; - S.push(v); - - for (const auto& w : G.adjMatrix[v]) { - if (p[w].index == -1) { +void Tarjan::strongConnect(const T& u) { + vmap[u].index = vmap[u].lowlink = index++; + S.push(u); + vmap[u].onStack = true; + + for (const auto& w : adjMatrix[u]) { + if (vmap[w].index == -1) { strongConnect(w); - p[v].lowlink = std::min(p[v].lowlink, p[w].lowlink); - } else if (p[w].onStack) { - p[v].lowlink = std::min(p[v].lowlink, p[w].index); + vmap[u].lowlink = std::min(vmap[u].lowlink, vmap[w].lowlink); + } else if (vmap[w].onStack) { + vmap[u].lowlink = std::min(vmap[u].lowlink, vmap[w].index); } } - // If v is a root node, pop the stack and generate an SCC - if (p[v].lowlink == p[v].index) { - //std::vector scc; + // If u is a root node, pop the stack and generate an SCC + if (vmap[u].lowlink == vmap[u].index) { std::map> scc; bool finished = false; + do { const auto w = S.top(); S.pop(); - p[w].onStack = false; - scc[w] = G.adjMatrix[w]; - finished = p[w].index == p[v].index; + vmap[w].onStack = false; + scc[w] = adjMatrix[w]; + finished = (w == u); } while (!finished); - SCCs.push_back(scc); + + SCCs.push_back({ scc, static_cast(vmap[u].lowlink) }); } } template -std::vector> Tarjan::execute() { - for (auto& v : G.adjMatrix) { - if (p[v.first].index == -1) { - strongConnect(v.first); - } +auto Tarjan::execute() { + for (const auto& u : std::views::keys(adjMatrix)) { + if (vmap[u].index == -1) + strongConnect(u); } - return SCCs; } +template +void Tarjan::setGraph(std::map> adjMatrix) { + this->adjMatrix = adjMatrix; +} + } // namespace algo #endif \ No newline at end of file diff --git a/test/algorithm_test.cc b/test/algorithm_test.cc index c827ae7..d15911c 100644 --- a/test/algorithm_test.cc +++ b/test/algorithm_test.cc @@ -3,79 +3,21 @@ #include "algorithm/tarjan.h" #include "algorithm/breadth_first_search.h" +#include +#include +constexpr int verticesNum = 10; + +#include + using namespace graph; TEST_SUITE("Algorithm") { - TEST_CASE("Tarjan::execute 1") { - // 1 --> 2 --> 4 --> 1 - // 2 --> 3 --> 5 --> 7 --> 3 - // 5 --> 9 --> 6 --> 8 --> 9 - Digraph G; - G.insert(1, 2); - G.insert(2, 3); - G.insert(2, 4); - 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.adjMatrix.size(), 9); - - algo::Tarjan tarjan(G); - auto r = tarjan.execute(); - - std::vector> x = { - {6, 8, 9}, - {3, 5, 7}, - {1, 2, 4} - }; - - for (auto i = 0; i < r.size(); i++) { - auto kv = std::views::keys(r[i].adjMatrix); - CHECK_EQ(std::is_permutation(kv.begin(), kv.end(), x[i].begin()), true); - } - } - - TEST_CASE("Tarjan::execute 2") { - // 1 --> 2 --> 5 --> 7 --> 2 - // 1 --> 4 --> 3 --> 1 - // 4 --> 6 --> 3 - Digraph G; - G.insert(1, 2); - G.insert(1, 4); - 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.adjMatrix.size(), 7); - - algo::Tarjan tarjan(G); - auto r = tarjan.execute(); - - std::vector> x = { - {2, 5, 7}, - {1, 3, 4, 6} - }; - - for (auto i = 0; i < r.size(); i++) { - auto kv = std::views::keys(r[i].adjMatrix); - CHECK_EQ(std::is_permutation(kv.begin(), kv.end(), x[i].begin()), true); - } - } - - TEST_CASE("Tarjan::execute 3") { + TEST_CASE("Tarjan::execute") { // 1 --> 2 --> 3 --> 1 // 3 --> 4 --> 5 --> 3 // 2 --> 6 --> 7 --> 8 --> 6 + // 7 --> 9 // 6 --> 9 --> 10 --> 11 --> 12 --> 13 --> 9 // 12 --> 10 Digraph G; @@ -100,53 +42,48 @@ TEST_SUITE("Algorithm") { REQUIRE_EQ(G.adjMatrix.size(), 13); - algo::Tarjan tarjan(G); - auto r = tarjan.execute(); + auto SCCs = algo::Tarjan(G.adjMatrix).execute(); - std::vector> x = { + std::vector> expected = { {9, 10, 11, 12, 13}, {6, 7, 8}, {1, 2, 3, 4, 5} }; - for (auto i = 0; i < r.size(); i++) { - auto kv = std::views::keys(r[i].adjMatrix); - CHECK_EQ(std::is_permutation(kv.begin(), kv.end(), x[i].begin()), true); + for (auto i = 0; i < SCCs.size(); i++) { + auto kv = std::views::keys(SCCs[i].adjMatrix); + CHECK_EQ(std::is_permutation(kv.begin(), kv.end(), + expected[i].begin()), true); } } - TEST_CASE("BreadthFirstSearch::execute 1") { - // 1 --> 2 --> 5 --> 7 --> 2 - // 1 --> 4 --> 3 --> 1 - // 4 --> 6 --> 3 + TEST_CASE("Tarjan::execute ~ DAG") { Digraph G; - G.insert(1, 2); - G.insert(1, 4); - 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); + auto gen = std::bind(std::uniform_real_distribution<>(0, 1), std::default_random_engine()); - algo::BreadthFirstSearch bfs(G); - auto r = bfs.execute(1); + for (int i = 0; i <= verticesNum; ++i) { + for (int j = i + 1; j <= verticesNum; ++j) { + if (gen() < 0.25) G.insert(i, j); + } + } - std::map> x = { - {1, {2, 4}}, - {2, {5}}, - {4, {3, 6}}, - {5, {7}} - }; + auto SCCs = algo::Tarjan(G.adjMatrix).execute(); - CHECK_EQ(r, x); + // Testing whether an scc is a 1-vertex scc which after the normalization + // has no edges, in this case we know tgat there exist no 2-vertex sccs + for (auto& scc : SCCs) { + CHECK_EQ(std::all_of(scc.adjMatrix.begin(), scc.adjMatrix.end(), + [](const auto& p) { + return p.second.size() == 0; + }), true); + } } - - TEST_CASE("BreadthFirstSearch::execute 2") { + + TEST_CASE("BreadthFirstSearch::execute") { // 1 --> 2 --> 3 --> 1 // 3 --> 4 --> 5 --> 3 // 2 --> 6 --> 7 --> 8 --> 6 + // 7 --> 9 // 6 --> 9 --> 10 --> 11 --> 12 --> 13 --> 9 // 12 --> 10 Digraph G; @@ -169,10 +106,10 @@ TEST_SUITE("Algorithm") { G.insert(13, 9); G.insert(12, 10); - algo::BreadthFirstSearch bfs(G); - auto r = bfs.execute(1); + auto tree = + algo::BreadthFirstSearch(G.adjMatrix).execute(1); - std::map> x = { + std::map> expected = { {1, {2}}, {2, {3, 6}}, {3, {4}}, @@ -185,6 +122,41 @@ TEST_SUITE("Algorithm") { {12, {13}} }; - CHECK_EQ(r, x); + CHECK_EQ(tree, expected); + } + + TEST_CASE("BreadthFirstSearch::query") { + // 1 --> 2 --> 3 --> 1 + // 3 --> 4 --> 5 --> 3 + // 2 --> 6 --> 7 --> 8 --> 6 + // 7 --> 9 + // 6 --> 9 --> 10 --> 11 --> 12 --> 13 --> 9 + // 12 --> 10 + Digraph G; + G.insert(1, 2); + G.insert(2, 3); + G.insert(3, 1); + G.insert(3, 4); + G.insert(4, 5); + G.insert(5, 3); + G.insert(2, 6); + G.insert(6, 7); + G.insert(7, 8); + G.insert(7, 9); + G.insert(8, 6); + G.insert(6, 9); + G.insert(9, 10); + G.insert(10, 11); + G.insert(11, 12); + G.insert(12, 13); + G.insert(13, 9); + G.insert(12, 10); + + algo::BreadthFirstSearch bfs(G.adjMatrix); + + CHECK_EQ(bfs.query(1, 5), true); + CHECK_EQ(bfs.query(1, 10), true); + CHECK_EQ(bfs.query(9, 5), false); + CHECK_EQ(bfs.query(8, 4), false); } } \ No newline at end of file