Make class Graph abstract and make derived classes Digraph and SCC

This commit is contained in:
stefiosif
2022-06-12 00:49:42 +03:00
parent b5b031db7f
commit 72741a6a5b
7 changed files with 113 additions and 113 deletions

View File

@@ -1,18 +1,18 @@
#ifndef BFS_H_ #ifndef BFS_H_
#define BFS_H_ #define BFS_H_
#include "graph/graph.h" #include "graph/digraph.h"
using namespace graph;
#include <algorithm>
#include <queue> #include <queue>
using namespace graph;
namespace algo { namespace algo {
template<typename T> template<typename T>
class BFS { class BFS {
public: public:
BFS(Graph<T> G, T root) : G(G), root(root) {} BFS(Digraph<T> G, T root) : G(G), root(root) {}
// //
std::map<T, std::set<T>> run(); std::map<T, std::set<T>> run();
@@ -20,7 +20,7 @@ public:
// //
std::map<T, bool> initExplore(); std::map<T, bool> initExplore();
private: private:
Graph<T> G; Digraph<T> G;
T root; T root;
}; };

View File

@@ -1,23 +1,23 @@
#ifndef TARJAN_H_ #ifndef TARJAN_H_
#define TARJAN_H_ #define TARJAN_H_
#include "graph/graph.h" #include "graph/digraph.h"
using namespace graph; #include "graph/scc.h"
#include <algorithm>
#include <iostream>
#include <ranges>
#include <stack> #include <stack>
#include <vector>
using namespace graph;
namespace algo { namespace algo {
template<typename T> template<typename T>
class Tarjan { class Tarjan {
public: public:
Tarjan(Graph<T> G) : G(G) {} Tarjan(Digraph<T> G) : G(G) {}
// //
std::vector<std::vector<T>> run(); std::vector<SCC<T>> run();
// //
void strongConnect(const T& v); void strongConnect(const T& v);
@@ -29,48 +29,48 @@ private:
bool onStack = false; bool onStack = false;
}; };
Graph<T> G; Digraph<T> G;
std::stack<T> S; std::stack<T> S;
std::int16_t index = 0; std::int16_t index = 0;
std::map<T, Payload> vp; std::map<T, Payload> p;
std::vector<std::vector<T>> SCCs; std::vector<SCC<T>> SCCs;
}; };
template<typename T> template<typename T>
void Tarjan<T>::strongConnect(const T& v) { void Tarjan<T>::strongConnect(const T& v) {
vp[v].index = vp[v].lowlink = index++; p[v].index = p[v].lowlink = index++;
vp[v].onStack = true; p[v].onStack = true;
S.push(v); S.push(v);
for (const auto& w : G.adjMatrix[v]) { for (const auto& w : G.adjMatrix[v]) {
if (vp[w].index == -1) { if (p[w].index == -1) {
strongConnect(w); strongConnect(w);
vp[v].lowlink = std::min(vp[v].lowlink, vp[w].lowlink); p[v].lowlink = std::min(p[v].lowlink, p[w].lowlink);
} else if (vp[w].onStack) { } else if (p[w].onStack) {
vp[v].lowlink = std::min(vp[v].lowlink, vp[w].index); p[v].lowlink = std::min(p[v].lowlink, p[w].index);
} }
} }
// If v is a root node, pop the stack and generate an SCC // If v is a root node, pop the stack and generate an SCC
if (vp[v].lowlink == vp[v].index) { if (p[v].lowlink == p[v].index) {
std::vector<T> SCC; //std::vector<T> scc;
std::map<T, std::set<T>> scc;
bool finished = false; bool finished = false;
do { do {
const auto w = S.top(); const auto w = S.top();
S.pop(); S.pop();
vp[w].onStack = false; p[w].onStack = false;
SCC.push_back(w); scc[w] = G.adjMatrix[w];
finished = vp[w].index == vp[v].index; finished = p[w].index == p[v].index;
} while (!finished); } while (!finished);
SCCs.push_back(SCC); SCCs.push_back(scc);
} }
} }
template<typename T> template<typename T>
std::vector<std::vector<T>> Tarjan<T>::run() { std::vector<SCC<T>> Tarjan<T>::run() {
for (const auto& v : G.vertices) { for (const auto& v : G.vertices) {
if (vp[v].index == -1) { if (p[v].index == -1) {
strongConnect(v); strongConnect(v);
} }
} }

26
graph/digraph.h Normal file
View File

@@ -0,0 +1,26 @@
#ifndef DIGRAPH_H_
#define DIGRAPH_H_
#include "graph.h"
#include <algorithm>
namespace graph {
template<typename T>
class Digraph : public Graph<T> {
public:
Digraph() = default;
// Reverse graph directions
Digraph<T> reverse();
};
template<typename T>
Digraph<T> Digraph<T>::reverse() {
return Digraph<T>();
}
} // namespace graph
#endif

View File

@@ -3,8 +3,6 @@
#include <map> #include <map>
#include <set> #include <set>
#include <utility>
#include <vector>
#include <iostream> #include <iostream>
namespace graph { namespace graph {
@@ -12,23 +10,19 @@ namespace graph {
template<typename T> template<typename T>
class Graph { class Graph {
public: public:
Graph() = default; ~Graph();
// Add vertex v
void insert(const T& v);
// Add edge between v and u // Add edge between v and u
void insert(const T& v, const T& u); virtual void insert(const T& v, const T& u);
// Reverse graph directions
Graph<T> reverse();
// Number of vertices // Number of vertices
std::uint16_t V(); virtual std::uint16_t V();
// Number of edges // Number of edges
// TODO: Calculate bidirectional edges once virtual std::uint16_t E();
std::uint16_t E();
// Output graph
virtual void output();
// Adjacency matrix representation // Adjacency matrix representation
std::set<T> vertices; std::set<T> vertices;
@@ -36,36 +30,44 @@ public:
}; };
template<typename T> template<typename T>
void Graph<T>::insert(const T& v) { Graph<T>::~Graph() {
vertices.insert(v); vertices.clear();
adjMatrix.clear();
} }
template<typename T> template<typename T>
void Graph<T>::insert(const T& v, const T& u) { inline void Graph<T>::insert(const T& v, const T& u) {
vertices.insert(v); Graph<T>::vertices.insert(v);
vertices.insert(u); Graph<T>::vertices.insert(u);
adjMatrix[v].insert(u); Graph<T>::adjMatrix[v].insert(u);
}
template<typename T>
Graph<T> Graph<T>::reverse() {
return Graph<T>();
} }
template<typename T> template<typename T>
std::uint16_t Graph<T>::V() { std::uint16_t Graph<T>::V() {
return vertices.size(); return static_cast<std::uint16_t>(vertices.size());
} }
template<typename T> template<typename T>
std::uint16_t Graph<T>::E() { std::uint16_t Graph<T>::E() {
std::uint16_t edges = 0; std::uint16_t edges = 0;
for (const auto& v : vertices) { for (const auto& v : vertices) {
edges += adjMatrix[v].size(); edges += static_cast<std::uint16_t>(adjMatrix[v].size());
} }
return edges; return static_cast<std::uint16_t>(edges);
}
template<typename T>
void Graph<T>::output() {
for (const auto& v : vertices) {
for (const auto& u : adjMatrix[v]) {
std::cout << v << "->" << u << '|';
}
std::cout << '\n';
}
std::cout << '\n';
} }
} // namespace graph } // namespace graph
#endif #endif

View File

@@ -1,45 +1,27 @@
#ifndef SCC_H_ #ifndef SCC_H_
#define SCC_H_ #define SCC_H_
#include "graph/graph.h" #include "graph.h"
#include <ranges>
namespace graph { namespace graph {
template<typename T> template<typename T>
class SCC { class SCC : public Graph<T> {
public: public:
SCC(std::vector<T> scc); SCC(std::map<T, std::set<T>> scc);
// Construct shortest path
std::vector<T> SPT();
// Convert SCC into a Graph
Graph<T> convert();
private: private:
std::vector<T> scc;
// Representative - Root(SPT) of this SCC
T root; T root;
}; };
template<typename T> template<typename T>
SCC<T>::SCC(std::vector<T> component) { SCC<T>::SCC(std::map<T, std::set<T>> scc) {
scc = component; Graph<T>::adjMatrix = scc;
root = scc[0]; auto kv = std::views::keys(Graph<T>::adjMatrix);
} Graph<T>::vertices = std::set<T>{ kv.begin(), kv.end() };
root = scc.begin()->first;
template<typename T>
std::vector<T> SCC<T>::SPT() {
// BFS
return std::vector<T>();
}
template<typename T>
Graph<T> SCC<T>::convert() {
return Graph<T>();
} }
}; // namespace graph }; // namespace graph

View File

@@ -1,17 +1,19 @@
#include <doctest/doctest.h> #include <doctest/doctest.h>
#include "graph/graph.h" #include "graph/digraph.h"
using namespace graph; #include "graph/scc.h"
#include "algorithm/tarjan.h" #include "algorithm/tarjan.h"
#include "algorithm/bfs.h" #include "algorithm/bfs.h"
using namespace graph;
TEST_SUITE("Algorithm.") { TEST_SUITE("Algorithm.") {
TEST_CASE("Tarjan T_1") { TEST_CASE("Tarjan T_1") {
// 1 --> 2 --> 4 --> 1 // 1 --> 2 --> 4 --> 1
// 2 --> 3 --> 5 --> 7 --> 3 // 2 --> 3 --> 5 --> 7 --> 3
// 5 --> 9 --> 6 --> 8 --> 9 // 5 --> 9 --> 6 --> 8 --> 9
Graph<std::uint16_t> G; Digraph<std::uint16_t> G;
G.insert(1, 2); G.insert(1, 2);
G.insert(2, 3); G.insert(2, 3);
G.insert(2, 4); G.insert(2, 4);
@@ -28,26 +30,23 @@ TEST_SUITE("Algorithm.") {
algo::Tarjan<std::uint16_t> tarjan(G); algo::Tarjan<std::uint16_t> tarjan(G);
auto result = tarjan.run(); auto result = tarjan.run();
std::sort(result.begin(), result.end()); for (auto& scc : result) {
std::for_each(result.begin(), result.end(), [&](std::vector<std::uint16_t>& row) { scc.output();
std::sort(row.begin(), row.end()); }
});
std::vector<std::vector<std::uint16_t>> expected{ std::vector<std::vector<std::uint16_t>> expected{
{1, 2, 4}, {1, 2, 4},
{3, 5, 7}, {3, 5, 7},
{6, 8, 9} {6, 8, 9}
}; };
CHECK_EQ(expected, result);
} }
TEST_CASE("Tarjan T_2") { TEST_CASE("Tarjan T_2") {
// 1 --> 2 --> 5 --> 7 --> 2 // 1 --> 2 --> 5 --> 7 --> 2
// 1 --> 4 --> 3 --> 1 // 1 --> 4 --> 3 --> 1
// 4 --> 6 --> 3 // 4 --> 6 --> 3
Graph<std::uint16_t> G; Digraph<std::uint16_t> G;
G.insert(1, 2); G.insert(1, 2);
G.insert(1, 4); G.insert(1, 4);
G.insert(2, 5); G.insert(2, 5);
@@ -63,24 +62,17 @@ TEST_SUITE("Algorithm.") {
algo::Tarjan<std::uint16_t> tarjan(G); algo::Tarjan<std::uint16_t> tarjan(G);
auto result = tarjan.run(); auto result = tarjan.run();
std::sort(result.begin(), result.end());
std::for_each(result.begin(), result.end(), [&](std::vector<std::uint16_t>& row) {
std::sort(row.begin(), row.end());
});
std::vector<std::vector<std::uint16_t>> expected{ std::vector<std::vector<std::uint16_t>> expected{
{1, 3, 4, 6}, {1, 3, 4, 6},
{2, 5, 7} {2, 5, 7}
}; };
CHECK_EQ(expected, result);
} }
TEST_CASE("BFS T_1") { TEST_CASE("BFS T_1") {
// 1 --> 2 --> 5 --> 7 --> 2 // 1 --> 2 --> 5 --> 7 --> 2
// 1 --> 4 --> 3 --> 1 // 1 --> 4 --> 3 --> 1
// 4 --> 6 --> 3 // 4 --> 6 --> 3
Graph<std::uint16_t> G; Digraph<std::uint16_t> G;
G.insert(1, 2); G.insert(1, 2);
G.insert(1, 4); G.insert(1, 4);
G.insert(2, 5); G.insert(2, 5);

View File

@@ -1,16 +1,20 @@
#include <doctest/doctest.h> #include <doctest/doctest.h>
#include "graph/scc.h" #include "graph/scc.h"
using namespace graph; #include "graph/digraph.h"
#include "algorithm/tarjan.h" #include "algorithm/tarjan.h"
#include <vector>
using namespace graph;
TEST_SUITE("Graph.") { TEST_SUITE("Graph.") {
TEST_CASE("SCC") { TEST_CASE("SCC") {
// 1 --> 2 --> 4 --> 1 // 1 --> 2 --> 4 --> 1
// 2 --> 3 --> 5 --> 7 --> 3 // 2 --> 3 --> 5 --> 7 --> 3
// 5 --> 9 --> 6 --> 8 --> 9 // 5 --> 9 --> 6 --> 8 --> 9
Graph<std::uint16_t> G; Digraph<std::uint16_t> G;
G.insert(1, 2); G.insert(1, 2);
G.insert(2, 3); G.insert(2, 3);
G.insert(2, 4); G.insert(2, 4);
@@ -24,12 +28,6 @@ TEST_SUITE("Graph.") {
G.insert(9, 6); G.insert(9, 6);
algo::Tarjan<std::uint16_t> tarjan(G); algo::Tarjan<std::uint16_t> tarjan(G);
auto tarjanOutput = tarjan.run(); auto SCCs = tarjan.run();
std::vector<SCC<std::uint16_t>> SCCs;
for (const auto& scc : tarjanOutput) {
SCCs.push_back(scc);
}
} }
} }