You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
345 lines
9.9 KiB
345 lines
9.9 KiB
#ifndef GRAPH_HPP
|
|
#define GRAPH_HPP
|
|
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
#include <algorithm>
|
|
#include <utility>
|
|
|
|
/**
|
|
the graph is:
|
|
- not weighed
|
|
- not directed. There are 2 edges for each connection, both direction
|
|
- no multi/self edges
|
|
|
|
- Stored as an unordered map where the keys are vertices and values are arrays of edges.
|
|
The multimap is picked since neighboursOf is the most critical operation.
|
|
|
|
- V expected to be cheap to copy
|
|
- V should have operator== and be hashable (for the internal std::unordered_map):
|
|
~~~{.cpp}
|
|
namespace std {
|
|
template <>
|
|
struct hash<A> {
|
|
std::size_t operator()(const A& a) const { return ...; }
|
|
};
|
|
}
|
|
~~~
|
|
*/
|
|
template <typename V>
|
|
class Graph {
|
|
|
|
public:
|
|
|
|
typedef size_t size_type;
|
|
typedef V value_type;
|
|
typedef V* pointer;
|
|
typedef const V* const_pointer;
|
|
typedef V& reference;
|
|
typedef const V& const_reference;
|
|
typedef std::ptrdiff_t difference_type;
|
|
|
|
private:
|
|
|
|
typedef std::vector<V> edge_container;
|
|
typedef std::unordered_map<V, edge_container> v_container;
|
|
typedef typename v_container::iterator v_iterator;
|
|
typedef typename v_container::const_iterator v_const_iterator;
|
|
|
|
public:
|
|
|
|
struct Edge {
|
|
Edge(const_reference s, const_reference d) : source(s), destination(d) {}
|
|
bool operator==(const Edge& o) const { return source == o.source && destination == o.destination; }
|
|
|
|
value_type source;
|
|
value_type destination;
|
|
};
|
|
|
|
Graph() : m_vertices() {}
|
|
Graph(std::initializer_list<V> vertex_list);
|
|
Graph(const std::vector<V>& vertex_list);
|
|
Graph(std::initializer_list<Edge> edge_list);
|
|
Graph(const std::vector<Edge>& edge_list);
|
|
bool operator==(const Graph& o) const;
|
|
bool operator!=(const Graph& o) const { return !(*this == o); }
|
|
|
|
void addVertex(const_reference data);
|
|
void removeVertex(const_reference data);
|
|
void modifyVertex(const_reference old_data, const_reference new_data);
|
|
|
|
void addEdge(const_reference source, const_reference destination);
|
|
void setEdges(const_reference source, const std::vector<value_type>& destinations);
|
|
void removeEdge(const_reference source, const_reference destination);
|
|
|
|
std::vector<value_type> vertices() const;
|
|
|
|
void clear() noexcept { m_vertices.clear(); }
|
|
const std::vector<value_type>& neighboursOf(const_reference data) const;
|
|
|
|
|
|
class vertex_iterator : public std::iterator<std::forward_iterator_tag,
|
|
value_type,
|
|
difference_type,
|
|
pointer,
|
|
reference>
|
|
{
|
|
friend class Graph;
|
|
|
|
public:
|
|
typedef vertex_iterator self_type;
|
|
typedef vertex_iterator& reference_self_type;
|
|
typedef const vertex_iterator& const_reference_self_type;
|
|
|
|
const_reference operator*() { return m_it->first; }
|
|
const_pointer operator->() { return &m_it->first; }
|
|
self_type &operator++() { ++m_it; return *this; }
|
|
self_type operator++(int) { self_type tmp(*this); ++(*this); return tmp; }
|
|
self_type operator+(difference_type n) { self_type tmp(*this); tmp.pos_ += n; return tmp; }
|
|
self_type &operator+=(difference_type n) { m_it += n; return *this; }
|
|
bool operator==(const_reference_self_type o) const { return m_it == o.m_it; }
|
|
bool operator!=(const_reference_self_type o) const { return !(*this == o); }
|
|
|
|
private:
|
|
vertex_iterator(v_iterator it) : m_it(it) {}
|
|
vertex_iterator(v_const_iterator it) : m_it(it) {}
|
|
|
|
v_const_iterator m_it;
|
|
};
|
|
|
|
typedef vertex_iterator iterator;
|
|
typedef const vertex_iterator const_iterator;
|
|
|
|
iterator begin() noexcept { return iterator(m_vertices.begin()); }
|
|
iterator begin() const noexcept { return iterator(m_vertices.begin()); }
|
|
const_iterator cbegin() const noexcept { return const_iterator(m_vertices.begin()); }
|
|
iterator end() noexcept { return iterator(m_vertices.end()); }
|
|
iterator end() const noexcept { return iterator(m_vertices.end()); }
|
|
const_iterator cend() const noexcept { return const_iterator(m_vertices.end()); }
|
|
|
|
private:
|
|
|
|
std::vector<value_type>& nonConstNeighboursOf(const_reference data);
|
|
static void eraseEdge(edge_container& v, const_reference data);
|
|
v_iterator addVertexAndReturnIterator(const_reference data);
|
|
|
|
v_container m_vertices;
|
|
};
|
|
|
|
// Free functions
|
|
|
|
template <typename V>
|
|
inline bool empty(const Graph<V>& g) noexcept { return g.vertices().empty(); }
|
|
|
|
template <typename V>
|
|
inline typename Graph<V>::size_type numberOfVertices(const Graph<V>& g) noexcept { return g.vertices().size(); }
|
|
|
|
template <typename V>
|
|
inline typename Graph<V>::size_type numberOfEdges(const Graph<V>& g) { return edges(g).size(); }
|
|
|
|
template <typename V>
|
|
inline bool contains(const Graph<V>& g, typename Graph<V>::const_reference data) {
|
|
const auto v = g.vertices();
|
|
return std::find(v.begin(), v.end(), data) != v.end();
|
|
}
|
|
|
|
template <typename V>
|
|
inline bool connected(const Graph<V>& g, typename Graph<V>::const_reference source, typename Graph<V>::const_reference destination) {
|
|
const auto n = g.neighboursOf(source);
|
|
return std::find(n.begin(), n.end(), destination) != n.end();
|
|
}
|
|
|
|
template <typename V>
|
|
inline std::vector<typename Graph<V>::Edge> edges(const Graph<V>& g)
|
|
{
|
|
std::vector<typename Graph<V>::Edge> retval;
|
|
for (const auto& v : g.vertices())
|
|
for (const auto& e : g.neighboursOf(v))
|
|
retval.emplace_back(typename Graph<V>::Edge(v, e));
|
|
|
|
return retval;
|
|
}
|
|
|
|
template <typename V>
|
|
inline Graph<V> disjointUnion(const Graph<V>& a, const Graph<V>& b) {
|
|
Graph<V> g(a);
|
|
for (const auto& e : edges(b))
|
|
g.addEdge(e.source, e.destination);
|
|
|
|
return g;
|
|
}
|
|
|
|
|
|
// Graph implementation
|
|
|
|
template <typename V>
|
|
inline Graph<V>::Graph(std::initializer_list<V> vertex_list)
|
|
: Graph<V>()
|
|
{
|
|
for(const auto& v : vertex_list)
|
|
addVertex(v);
|
|
}
|
|
|
|
template <typename V>
|
|
inline Graph<V>::Graph(const std::vector<V>& vertex_list)
|
|
: Graph<V>()
|
|
{
|
|
for(const auto& v : vertex_list)
|
|
addVertex(v);
|
|
}
|
|
|
|
template <typename V>
|
|
inline Graph<V>::Graph(std::initializer_list<Edge> edge_list)
|
|
: Graph<V>()
|
|
{
|
|
for (const auto& e : edge_list )
|
|
addEdge(e.source, e.destination);
|
|
}
|
|
|
|
template <typename V>
|
|
inline Graph<V>::Graph(const std::vector<Edge>& edge_list)
|
|
: Graph<V>()
|
|
{
|
|
for (const auto& e : edge_list )
|
|
addEdge(e.source, e.destination);
|
|
}
|
|
|
|
template <typename V>
|
|
inline bool Graph<V>::operator==(const Graph& o) const
|
|
{
|
|
if (numberOfVertices(*this) != numberOfVertices(o))
|
|
return false;
|
|
|
|
for (const auto& v : m_vertices) {
|
|
const auto& o_v(o.neighboursOf(v.first));
|
|
if (v.second.size() != o_v.size())
|
|
return false;
|
|
|
|
// compare 2 unordered vectors of unique elements...very costy
|
|
/// @note should vector be replaced with set?
|
|
auto v_copy(v.second);
|
|
auto o_v_copy(o_v);
|
|
sort(v_copy.begin(), v_copy.end());
|
|
sort(o_v_copy.begin(), o_v_copy.end());
|
|
if (v_copy != o_v_copy)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
template <typename V>
|
|
inline void Graph<V>::addVertex(const_reference data)
|
|
{
|
|
addVertexAndReturnIterator(data);
|
|
}
|
|
|
|
template <typename V>
|
|
inline void Graph<V>::removeVertex(const_reference data)
|
|
{
|
|
const auto number_of_removed_elements = m_vertices.erase(data);
|
|
if (number_of_removed_elements > 0)
|
|
for (auto &v : m_vertices)
|
|
eraseEdge(v.second, data);
|
|
}
|
|
|
|
template <typename V>
|
|
inline void Graph<V>::modifyVertex(const_reference old_data, const_reference new_data)
|
|
{
|
|
if (old_data == new_data)
|
|
return;
|
|
|
|
auto neighbours = neighboursOf(old_data);
|
|
for (auto &v : neighbours) {
|
|
auto& n = nonConstNeighboursOf(v);
|
|
auto n_it = std::find(n.begin(), n.end(), old_data);
|
|
*n_it = new_data;
|
|
}
|
|
|
|
const auto number_of_removed_elements = m_vertices.erase(old_data);
|
|
if (number_of_removed_elements > 0)
|
|
m_vertices.emplace(new_data, neighbours);
|
|
}
|
|
|
|
template <typename V>
|
|
inline void Graph<V>::addEdge(const_reference source, const_reference destination)
|
|
{
|
|
if (source == destination) // no self-edges
|
|
return;
|
|
|
|
auto n = neighboursOf(source); // no multiedges
|
|
if (std::find(n.begin(), n.end(), destination) != n.end())
|
|
return;
|
|
|
|
auto source_it = addVertexAndReturnIterator(source);
|
|
source_it->second.push_back(destination);
|
|
auto destination_it = addVertexAndReturnIterator(destination);
|
|
destination_it->second.push_back(source);
|
|
}
|
|
|
|
template <typename V>
|
|
inline void Graph<V>::setEdges(const_reference source, const std::vector<value_type>& destinations)
|
|
{
|
|
auto source_it = addVertexAndReturnIterator(source);
|
|
source_it->second.clear();
|
|
source_it->second = destinations;
|
|
}
|
|
|
|
template <typename V>
|
|
inline void Graph<V>::removeEdge(const_reference source, const_reference destination)
|
|
{
|
|
auto source_it = m_vertices.find(source);
|
|
if (source_it == m_vertices.end())
|
|
return;
|
|
|
|
auto destination_it = m_vertices.find(destination);
|
|
if (destination_it == m_vertices.end())
|
|
return;
|
|
|
|
eraseEdge(source_it->second, destination);
|
|
eraseEdge(destination_it->second, source);
|
|
}
|
|
|
|
template <typename V>
|
|
inline std::vector<typename Graph<V>::value_type> Graph<V>::vertices() const
|
|
{
|
|
std::vector<value_type> retval;
|
|
for (const auto& v : m_vertices)
|
|
retval.push_back(v.first);
|
|
|
|
return retval;
|
|
}
|
|
|
|
template <typename V>
|
|
inline const std::vector<V>& Graph<V>::neighboursOf(const_reference data) const
|
|
{
|
|
static std::vector<V> empty;
|
|
auto vertex_it = m_vertices.find(data);
|
|
if (vertex_it == m_vertices.end())
|
|
return empty;
|
|
else
|
|
return vertex_it->second;
|
|
}
|
|
|
|
template <typename V>
|
|
inline std::vector<V>& Graph<V>::nonConstNeighboursOf(const_reference data)
|
|
{
|
|
auto vertex_it = m_vertices.find(data);
|
|
return vertex_it->second;
|
|
}
|
|
|
|
template <typename V>
|
|
inline void Graph<V>::eraseEdge(edge_container& v, const_reference data) {
|
|
v.erase(std::remove_if(v.begin(), v.end(),
|
|
[&data](const_reference d) { return d == data; }),
|
|
v.end());
|
|
}
|
|
|
|
template <typename V>
|
|
inline typename Graph<V>::v_iterator Graph<V>::addVertexAndReturnIterator(const_reference data)
|
|
{
|
|
return m_vertices.emplace(data, edge_container()).first;
|
|
}
|
|
#endif // GRAPH_HPP
|