diff --git a/graph/breadth_first_tree.h b/graph/breadth_first_tree.h new file mode 100644 index 0000000..296ef04 --- /dev/null +++ b/graph/breadth_first_tree.h @@ -0,0 +1,28 @@ +#ifndef BREADTH_FIRST_TREE_H_ +#define BREADTH_FIRST_TREE_H_ + +#include "digraph.h" + +namespace graph { + +template +class BreadthFirstTree : public Digraph { +public: + BreadthFirstTree() = default; + + BreadthFirstTree(std::map> G, T root) + : BreadthFirstTree(Digraph(G), root) {} + + BreadthFirstTree(Digraph G, T root); + + T root; +}; + +template +BreadthFirstTree::BreadthFirstTree(Digraph G, T root) { + this->adjMatrix = algo::BreadthFirstSearch(G.adjMatrix).execute(root); +} + +} // namespace graph + +#endif \ No newline at end of file diff --git a/graph/digraph.h b/graph/digraph.h index c10f1b6..4880d6f 100644 --- a/graph/digraph.h +++ b/graph/digraph.h @@ -2,9 +2,7 @@ #define DIGRAPH_H_ #include "graph.h" - -#include -#include +#include "algorithm/breadth_first_search.h" namespace graph { @@ -13,24 +11,49 @@ class Digraph : public Graph { public: Digraph() = default; - Digraph(std::map> digraph); + Digraph(std::map> G); + + // Return true if there is a path from u to v + bool contains(const T& u, const T& v); + + // Add edge e(u,v) + void insert(const T& u, const T& v); + + // Remove edge e(u,v) + void remove(const T& u, const T& v); // Reverse graph directions auto reverse(); }; template -Digraph::Digraph(std::map> digraph) { - Graph::adjMatrix = digraph; +Digraph::Digraph(std::map> G) { + this->adjMatrix = G; +} + +template +bool Digraph::contains(const T& u, const T& v) { + return algo::BreadthFirstSearch().query(u, v); +} + +template +void Digraph::insert(const T& u, const T& v) { + this->adjMatrix[u].insert(v); + this->adjMatrix[v]; +} + +template +void Digraph::remove(const T& u, const T& v) { + this->adjMatrix[u].erase(v); } template auto Digraph::reverse() { std::map> revMatrix; - for (const auto& v : Graph::adjMatrix) { - for (const auto& u : v.second) { - revMatrix[u].insert(v.first); + for (const auto& u : this->adjMatrix) { + for (const auto& v : u.second) { + revMatrix[v].insert(u.first); } } diff --git a/graph/graph.h b/graph/graph.h index f688e96..e67bd27 100644 --- a/graph/graph.h +++ b/graph/graph.h @@ -3,38 +3,38 @@ #include #include -#include +#include namespace graph { +// Forward declerations +template class Graph; +template std::ostream& operator<<(std::ostream& os, Graph& G); + template class Graph { public: ~Graph(); + + // Return true if there is a path from u to v + virtual bool contains(const T& u, const T& v) = 0; // Add edge e(u,v) - virtual void insert(const T& u, const T& v); - - // Return true if e(u,v) exists - virtual bool contains(const T& u, const T& v); - - // Return true if vertex u exists - virtual bool contains(const T& u); + virtual void insert(const T& u, const T& v) = 0; // Remove edge e(u,v) - virtual void remove(const T& u, const T& v); + virtual void remove(const T& u, const T& v) = 0; // Return num. of vertices - virtual std::uint16_t V(); + std::uint16_t V(); // Return num. of edges - virtual std::uint16_t E(); - - // Output graph - virtual void output(); + std::uint16_t E(); // Adjacency matrix representation std::map> adjMatrix; + + friend std::ostream& operator<<<>(std::ostream& os, Graph& G); }; template @@ -42,27 +42,6 @@ Graph::~Graph() { adjMatrix.clear(); } -template -inline void Graph::insert(const T& u, const T& v) { - Graph::adjMatrix[u].insert(v); - Graph::adjMatrix[v]; -} - -template -bool Graph::contains(const T& u, const T& v) { - return adjMatrix[u].contains(v); -} - -template -bool Graph::contains(const T& u) { - return adjMatrix.contains(u); -} - -template -void Graph::remove(const T& u, const T& v) { - adjMatrix[u].erase(v); -} - template std::uint16_t Graph::V() { return static_cast(adjMatrix.size()); @@ -78,14 +57,18 @@ std::uint16_t Graph::E() { } template -void Graph::output() { - for (const auto& v : adjMatrix) { - for (const auto& u : v.second) { - std::cout << v.first << "->" << u << '|'; +std::ostream& operator<<(std::ostream& os, Graph& G) { + os << "V: " << G.V() << " E: " << G.E() << '\n'; + for (const auto& u : G.adjMatrix) { + if (!u.second.empty()) { + for (const auto& v : u.second) { + os << u.first << "->" << v << ' '; + } + os << '\n'; } - std::cout << '\n'; } - std::cout << '\n'; + os << '\n'; + return os; } } // namespace graph diff --git a/graph/scc.h b/graph/scc.h index 83760e9..94592e2 100644 --- a/graph/scc.h +++ b/graph/scc.h @@ -3,8 +3,6 @@ #include "digraph.h" -#include - namespace graph { template @@ -12,22 +10,34 @@ class SCC : public Digraph { public: SCC() = default; - SCC(std::map> scc) - : Digraph::Digraph(scc) { proxy = scc.begin()->first; } + SCC(std::map> G, T id) : Digraph(G), id(id) { normalize(); } - // - T representative() { return proxy; }; + SCC(Digraph G, T id) : id(id) { normalize(); } + + T id; bool operator==(const SCC& o) const; private: - // Each SCC has a representative vertex that helps - // answer strong connectivity queries in O(1) time - T proxy; + // For every vertex in the SCC, if there is an edge between an in-scc vertex + // and an out-scc vertex erase the edge between them and the out-scc vertex + void normalize(); }; +template +void SCC::normalize() { + for (const auto& u : this->adjMatrix) { + for (const auto& v : u.second) { + if (!this->adjMatrix.count(v)) { + this->adjMatrix[u.first].erase(v); + this->adjMatrix.erase(v); + } + } + } +} + template bool SCC::operator==(const SCC& o) const{ - return proxy == o.proxy; + return id == o.id; } }; // namespace graph diff --git a/test/graph_test.cc b/test/graph_test.cc index e6d957d..5c8be0d 100644 --- a/test/graph_test.cc +++ b/test/graph_test.cc @@ -10,48 +10,63 @@ using namespace graph; TEST_SUITE("Graph") { TEST_CASE("Digraph::reverse") { - // 1 --> 2 --> 4 --> 1 - // 2 --> 3 --> 5 --> 7 --> 3 - // 5 --> 9 --> 6 --> 8 --> 9 + // 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(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); + 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); - REQUIRE_EQ(G.adjMatrix.size(), 9); + REQUIRE_EQ(G.adjMatrix.size(), 13); - auto R = G.reverse(); + auto reverse = G.reverse(); - std::map> X = { - {1, {4}}, + std::map> expected = { + {1, {3}}, {2, {1}}, - {3, {2, 7}}, - {4, {2}}, - {5, {3}}, - {6, {9}}, - {7, {5}}, - {8, {6}}, - {9, {5, 8}} + {3, {2, 5}}, + {4, {3}}, + {5, {4}}, + {6, {2, 8}}, + {7, {6}}, + {8, {7}}, + {9, {6, 7, 13}}, + {10, {9, 12}}, + {11, {10}}, + {12, {11}}, + {13, {12}} }; - CHECK_EQ(R, X); + CHECK_EQ(reverse, expected); } TEST_CASE("Graph::V and Graph::E") { // 1 --> 2 --> 3 --> 1 // 3 --> 4 --> 5 --> 3 // 2 --> 6 --> 7 --> 8 --> 6 + // 7 --> 9 // 6 --> 9 --> 10 --> 11 --> 12 --> 13 --> 9 // 12 --> 10 - Graph G; + Digraph G; G.insert(1, 2); G.insert(2, 3); G.insert(3, 1); diff --git a/test/tree_test.cc b/test/tree_test.cc deleted file mode 100644 index fb27811..0000000 --- a/test/tree_test.cc +++ /dev/null @@ -1,36 +0,0 @@ -#include - -#include "tree/breadth_first_tree.h" - -using namespace graph; -using namespace tree; - -TEST_SUITE("Tree") { - TEST_CASE("Breadth First Tree") { - - // 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); - - BreadthFirstTree tree(G, 1); - - std::map> exp = { - {1, {2, 4}}, - {2, {5}}, - {4, {3, 6}}, - {5, {7}} - }; - - CHECK_EQ(tree.adjMatrix, exp); - } -} \ No newline at end of file diff --git a/tree/breadth_first_tree.h b/tree/breadth_first_tree.h deleted file mode 100644 index 0a3beeb..0000000 --- a/tree/breadth_first_tree.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef BREADTH_FIRST_TREE_H_ -#define BREADTH_FIRST_TREE_H_ - -#include "directed_rooted_tree.h" -#include "algorithm/breadth_first_search.h" -using namespace graph; - -namespace tree { - -template -class BreadthFirstTree : public DirectedRootedTree { -public: - BreadthFirstTree() = default; - - BreadthFirstTree(Digraph G, T root); - - BreadthFirstTree(std::map> G) - : DirectedRootedTree::DirectedRootedTree(G) {} -}; - -template -BreadthFirstTree::BreadthFirstTree(Digraph G, T root) { - auto bfs = algo::BreadthFirstSearch(G).execute(root); - this->adjMatrix = bfs; -} - -} // namespace tree - -#endif \ No newline at end of file diff --git a/tree/directed_rooted_tree.h b/tree/directed_rooted_tree.h deleted file mode 100644 index c8b77ce..0000000 --- a/tree/directed_rooted_tree.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef DIRECTED_ROOTED_TREE_H_ -#define DIRECTED_ROOTED_TREE_H_ - -#include "tree.h" -using namespace graph; - -namespace tree { - -template -class DirectedRootedTree : public Tree { -public: - DirectedRootedTree() = default; - - DirectedRootedTree(std::map> G) - : Tree::Tree(G) {} -}; - -} // namespace tree - -#endif \ No newline at end of file diff --git a/tree/tree.h b/tree/tree.h deleted file mode 100644 index 44447ce..0000000 --- a/tree/tree.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef TREE_H_ -#define TREE_H_ - -#include "graph/digraph.h" -using namespace graph; - -namespace tree { - -template -class Tree : public Digraph { -public: - Tree() = default; - - Tree(std::map> G) : Digraph::Digraph(G) {} -}; - -} // namespace tree - -#endif \ No newline at end of file