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

View File

@@ -1,23 +1,23 @@
#ifndef TARJAN_H_
#define TARJAN_H_
#include "graph/graph.h"
using namespace graph;
#include "graph/digraph.h"
#include "graph/scc.h"
#include <algorithm>
#include <iostream>
#include <ranges>
#include <stack>
#include <vector>
using namespace graph;
namespace algo {
template<typename T>
class Tarjan {
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);
@@ -29,48 +29,48 @@ private:
bool onStack = false;
};
Graph<T> G;
Digraph<T> G;
std::stack<T> S;
std::int16_t index = 0;
std::map<T, Payload> vp;
std::vector<std::vector<T>> SCCs;
std::map<T, Payload> p;
std::vector<SCC<T>> SCCs;
};
template<typename T>
void Tarjan<T>::strongConnect(const T& v) {
vp[v].index = vp[v].lowlink = index++;
vp[v].onStack = true;
p[v].index = p[v].lowlink = index++;
p[v].onStack = true;
S.push(v);
for (const auto& w : G.adjMatrix[v]) {
if (vp[w].index == -1) {
if (p[w].index == -1) {
strongConnect(w);
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);
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);
}
}
// If v is a root node, pop the stack and generate an SCC
if (vp[v].lowlink == vp[v].index) {
std::vector<T> SCC;
if (p[v].lowlink == p[v].index) {
//std::vector<T> scc;
std::map<T, std::set<T>> scc;
bool finished = false;
do {
const auto w = S.top();
S.pop();
vp[w].onStack = false;
SCC.push_back(w);
finished = vp[w].index == vp[v].index;
p[w].onStack = false;
scc[w] = G.adjMatrix[w];
finished = p[w].index == p[v].index;
} while (!finished);
SCCs.push_back(SCC);
SCCs.push_back(scc);
}
}
template<typename T>
std::vector<std::vector<T>> Tarjan<T>::run() {
std::vector<SCC<T>> Tarjan<T>::run() {
for (const auto& v : G.vertices) {
if (vp[v].index == -1) {
if (p[v].index == -1) {
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 <set>
#include <utility>
#include <vector>
#include <iostream>
namespace graph {
@@ -12,23 +10,19 @@ namespace graph {
template<typename T>
class Graph {
public:
Graph() = default;
// Add vertex v
void insert(const T& v);
~Graph();
// Add edge between v and u
void insert(const T& v, const T& u);
// Reverse graph directions
Graph<T> reverse();
virtual void insert(const T& v, const T& u);
// Number of vertices
std::uint16_t V();
virtual std::uint16_t V();
// Number of edges
// TODO: Calculate bidirectional edges once
std::uint16_t E();
virtual std::uint16_t E();
// Output graph
virtual void output();
// Adjacency matrix representation
std::set<T> vertices;
@@ -36,36 +30,44 @@ public:
};
template<typename T>
void Graph<T>::insert(const T& v) {
vertices.insert(v);
Graph<T>::~Graph() {
vertices.clear();
adjMatrix.clear();
}
template<typename T>
void Graph<T>::insert(const T& v, const T& u) {
vertices.insert(v);
vertices.insert(u);
adjMatrix[v].insert(u);
}
template<typename T>
Graph<T> Graph<T>::reverse() {
return Graph<T>();
inline void Graph<T>::insert(const T& v, const T& u) {
Graph<T>::vertices.insert(v);
Graph<T>::vertices.insert(u);
Graph<T>::adjMatrix[v].insert(u);
}
template<typename T>
std::uint16_t Graph<T>::V() {
return vertices.size();
return static_cast<std::uint16_t>(vertices.size());
}
template<typename T>
std::uint16_t Graph<T>::E() {
std::uint16_t edges = 0;
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
#endif

View File

@@ -1,45 +1,27 @@
#ifndef SCC_H_
#define SCC_H_
#include "graph/graph.h"
#include "graph.h"
#include <ranges>
namespace graph {
template<typename T>
class SCC {
class SCC : public Graph<T> {
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:
std::vector<T> scc;
// Representative - Root(SPT) of this SCC
T root;
};
template<typename T>
SCC<T>::SCC(std::vector<T> component) {
scc = component;
root = scc[0];
}
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>();
SCC<T>::SCC(std::map<T, std::set<T>> scc) {
Graph<T>::adjMatrix = scc;
auto kv = std::views::keys(Graph<T>::adjMatrix);
Graph<T>::vertices = std::set<T>{ kv.begin(), kv.end() };
root = scc.begin()->first;
}
}; // namespace graph

View File

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

View File

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