diff --git a/src/main/cpp/algorithms/graphtheory/ArticulationPointsAdjacencyList.h b/src/main/cpp/algorithms/graphtheory/ArticulationPointsAdjacencyList.h new file mode 100644 index 0000000..19714b0 --- /dev/null +++ b/src/main/cpp/algorithms/graphtheory/ArticulationPointsAdjacencyList.h @@ -0,0 +1,117 @@ +/* + * @file ArticulationPointsAdjacencyList.h + * @author (original JAVA) William Fiset, william.alexandre.fiset@gmail.com + * (conversion to C++) mingy, enyala9733@gmail.com + * @date 21 November 2023 + * @version 0.1 + * @brief An implementation of Tarjan's algorithm to find ArticulationPoints using an adjacency list. + * Time complexity: O(V+E) + * + */ +#ifndef D_GRAPH_ARTICULATIONPOINT_H +#define D_GRAPH_ARTICULATIONPOINT_H + +#include +#include +#include +#include +#include // set and multiset +#include // map and multimap +#include // unordered set/multiset +#include // unordered map/multimap +#include +#include +#include // some numeric algorithm +#include +#include + +#include +#include +#include +#include +#include +#include +namespace dsa { + using std::vector; + using std::min; + using std::max; + class ArticulationPointsAdjacencyList { + private: + int n, id, rootNodeOutcomingEdgeCount; + bool solved; + vectorlow, ids; + vectorvisited, isArticulationPoint; + Graph* graph; + public: + ArticulationPointsAdjacencyList(Graph* graph_) { + if (graph_ == nullptr) throw std::invalid_argument("GRAPH NULL"); + n = graph_->size(); + if (n == 0)throw std::invalid_argument("GRAPH NULL"); + graph = graph_; + } + + // Returns the indexes for all articulation points in the graph even if the + // graph is not fully connected. + vector findArticulationPoints() { + if (solved) return isArticulationPoint; + id = 0; + low.resize(n); // Low link values + ids.resize(n); // Nodes ids + visited.resize(n); + isArticulationPoint.resize(n); + for (int i = 0; i < n; i++) { + if (!visited[i]) { + rootNodeOutcomingEdgeCount = 0; + dfs(i, i, -1); + isArticulationPoint[i] = (rootNodeOutcomingEdgeCount > 1); + } + } + solved = true; + return isArticulationPoint; + } + void dfs(int root, int at, int parent) { + if (parent == root) rootNodeOutcomingEdgeCount++; + visited[at] = true; + low[at] = ids[at] = id++; + + vector edges = graph->getNebWithoutWeight(at); + for (int to : edges) { + if (to == parent) continue; + if (!visited[to]) { + dfs(root, to, at); + low[at] = min(low[at], low[to]); + if (ids[at] <= low[to]) { + isArticulationPoint[at] = true; + } + } + else { + low[at] = min(low[at], ids[to]); + } + } + } + }; +} + +int ArticulationPointsTest() { + int n = 9; + Graph graph(n); + graph.addUndirectedEdge(0, 1); + graph.addUndirectedEdge(0, 2); + graph.addUndirectedEdge(1, 2); + graph.addUndirectedEdge(2, 3); + graph.addUndirectedEdge(3, 4); + graph.addUndirectedEdge(2, 5); + graph.addUndirectedEdge(5, 6); + graph.addUndirectedEdge(6, 7); + graph.addUndirectedEdge(7, 8); + graph.addUndirectedEdge(8, 5); + std::unique_ptr solver = + std::make_unique(&graph); + auto articulation_point = solver->findArticulationPoints(); + for (int i = 0; i < articulation_point.size(); ++i) + if (articulation_point[i])cout << i << " "; + + solver = nullptr; + return 0; +} +#endif diff --git a/src/main/cpp/algorithms/graphtheory/Graph.h b/src/main/cpp/algorithms/graphtheory/Graph.h index 65eadce..a9040c4 100644 --- a/src/main/cpp/algorithms/graphtheory/Graph.h +++ b/src/main/cpp/algorithms/graphtheory/Graph.h @@ -29,176 +29,189 @@ namespace dsa { -typedef std::vector IntegerVector; + typedef std::vector IntegerVector; -class Graph -{ + class Graph + { -public: + public: - using GRAPH_EDGE = std::unordered_map; // Node ID, Cost - using GRAPH_VERTEX = std::unordered_map; // Node ID, Edges + using GRAPH_EDGE = std::unordered_map; // Node ID, Cost + using GRAPH_VERTEX = std::unordered_map; // Node ID, Edges -private: - GRAPH_VERTEX *vertices_; - int edgeCount_; + private: + GRAPH_VERTEX* vertices_; + int edgeCount_; -public: - Graph(int n) { - vertices_ = new GRAPH_VERTEX(); - for (int i = 0; i < n; i++) { - GRAPH_EDGE edge_; - vertices_->insert(make_pair(i, std::move(edge_))); + public: + Graph(int n) { + vertices_ = new GRAPH_VERTEX(); + for (int i = 0; i < n; i++) { + GRAPH_EDGE edge_; + vertices_->insert(make_pair(i, std::move(edge_))); + } + edgeCount_ = 0; } - edgeCount_ = 0; - } - virtual ~Graph() { - if (vertices_) { - delete vertices_; - vertices_ = nullptr; - } - } + virtual ~Graph() { + if (vertices_) { + delete vertices_; + vertices_ = nullptr; + } + } - Graph(const Graph& rhs) { // Copy constructor - std::cout << "Graph copy constructor" << std::endl; - vertices_ = rhs.vertices_; - edgeCount_ = rhs.edgeCount_; - } + Graph(const Graph& rhs) { // Copy constructor + std::cout << "Graph copy constructor" << std::endl; + vertices_ = rhs.vertices_; + edgeCount_ = rhs.edgeCount_; + } - Graph(const Graph&& rhs) { // Move constructor - std::cout << "Graph move constructor" << std::endl; - vertices_ = rhs.vertices_; - edgeCount_ = rhs.edgeCount_; - } + Graph(const Graph&& rhs) { // Move constructor + std::cout << "Graph move constructor" << std::endl; + vertices_ = rhs.vertices_; + edgeCount_ = rhs.edgeCount_; + } - Graph& operator = (const Graph& rhs ) { - std::cout << "Graph copy operator" << std::endl; - if (this != &rhs) { - for (auto it = rhs.vertices_->begin();; it++) { - if (it == rhs.vertices_->end()) break; - GRAPH_EDGE edges; - if (!it->second.empty()) { - for (auto edge: it->second) { - edges.insert(std::move(edge)); - } - } - vertices_->insert(make_pair(it->first, std::move(edges))); + Graph& operator = (const Graph& rhs) { + std::cout << "Graph copy operator" << std::endl; + if (this != &rhs) { + for (auto it = rhs.vertices_->begin();; it++) { + if (it == rhs.vertices_->end()) break; + GRAPH_EDGE edges; + if (!it->second.empty()) { + for (auto edge : it->second) { + edges.insert(std::move(edge)); + } + } + vertices_->insert(make_pair(it->first, std::move(edges))); + } } - } - std::cout << "Graph copy operator DONE" << std::endl; + std::cout << "Graph copy operator DONE" << std::endl; - return *this; - } + return *this; + } - Graph& operator = (Graph&& rhs ) { - std::cout << "Graph move operator" << std::endl; - if (this != &rhs) { - vertices_ = rhs.vertices_; - } - return *this; - } - - GRAPH_VERTEX* operator()() { - return vertices_; - } - - - // Empty this graph - void clear() { - for (auto itr = vertices_->begin(); itr != vertices_->end(); itr++) - itr->second.erase(itr->second.begin(), itr->second.end()); - vertices_->erase(vertices_->begin(), vertices_->end()); - } - - - // Get size of graph - unsigned int size() { - return vertices_->size(); - } - - - // Get number of edges - unsigned int edgeCount() { - return edgeCount_; - } - - - // Add a directed edge from node 'u' to node 'v' with cost 'cost'. - // - // Adds a directed edge to the graph. - // - // @param from - The index of the node the directed edge starts at. - // @param to - The index of the node the directed edge end at. - // @param cost - The cost of the edge. - // - void addDirectedEdge(int u, int v, double cost = 0.0) { - (*vertices_)[u][v] = cost; - edgeCount_++; - } - - - // Add an undirected edge between nodes 'u' and 'v'. - void addUndirectedEdge(int u, int v, double cost = 0.0) { - addDirectedEdge(u, v, cost); - addDirectedEdge(v, u, cost); - } - - - // Add an undirected un-weighted edge between nodes 'u' and 'v'. The edge added - // will have a weight of 1 since its intended to be unweighted. - void addUnweightedUndirectedEdge(int u, int v) { - addUndirectedEdge(u, v, 0.0); - } - - - static std::string formatPath(int start, int end, std::list& path) { - std::stringstream ss; - ss << "Path(" << start << ":" << end << ")["; - if (path.size()) { - for (auto node: path) { - ss << " -> "; - ss << node; + Graph& operator = (Graph&& rhs) { + std::cout << "Graph move operator" << std::endl; + if (this != &rhs) { + vertices_ = rhs.vertices_; } + return *this; + } + + GRAPH_VERTEX* operator()() { + return vertices_; } - ss << "]"; - std::string s = ss.str(); - return s; - } + // Empty this graph + void clear() { + for (auto itr = vertices_->begin(); itr != vertices_->end(); itr++) + itr->second.erase(itr->second.begin(), itr->second.end()); + vertices_->erase(vertices_->begin(), vertices_->end()); + } - std::string toString() const { - std::stringstream os; - os << "Graph[" << std::endl; - for (auto it = vertices_->begin();; it++) { - if (it == vertices_->end()) break; - os << " Node(" << it->first << ")["; - if (!it->second.empty()) { - for (auto edge: it->second) { - os << "Edge(" << "->" << edge.first << ",cost:" << edge.second << ")"; - os << ","; - } - } - os << "]" << std::endl; + // Get size of graph + unsigned int size() { + return vertices_->size(); } - os << "]"; - return os.str(); - } + // Get number of edges + unsigned int edgeCount() { + return edgeCount_; + } + + // Add a directed edge from node 'u' to node 'v' with cost 'cost'. + // + // Adds a directed edge to the graph. + // + // @param from - The index of the node the directed edge starts at. + // @param to - The index of the node the directed edge end at. + // @param cost - The cost of the edge. + // + void addDirectedEdge(int u, int v, double cost = 0.0) { + (*vertices_)[u][v] = cost; + edgeCount_++; + } + + + // Add an undirected edge between nodes 'u' and 'v'. + void addUndirectedEdge(int u, int v, double cost = 0.0) { + addDirectedEdge(u, v, cost); + addDirectedEdge(v, u, cost); + } - friend std::ostream& operator<<(std::ostream &strm, const Graph &g) { - return strm << g.toString(); - } -}; + // Add an undirected un-weighted edge between nodes 'u' and 'v'. The edge added + // will have a weight of 1 since its intended to be unweighted. + void addUnweightedUndirectedEdge(int u, int v) { + addUndirectedEdge(u, v, 0.0); + } + + std::vector getNebWithoutWeight(int node) + { + auto nebs = (*vertices_)[node]; + std::vector res; + for (auto [neb, w] : nebs) res.push_back(neb); + return res; + } + std::vector> getNebWithWeight(int node) + { + auto nebs = (*vertices_)[node]; + std::vector> res; + for (auto [neb, w] : nebs) res.push_back(std::make_pair(neb, w)); + return res; + } + static std::string formatPath(int start, int end, std::list& path) { + std::stringstream ss; + ss << "Path(" << start << ":" << end << ")["; + if (path.size()) { + for (auto node : path) { + ss << " -> "; + ss << node; + } + } + ss << "]"; + std::string s = ss.str(); + + return s; + } + + + std::string toString() const { + std::stringstream os; + os << "Graph[" << std::endl; + + for (auto it = vertices_->begin();; it++) { + if (it == vertices_->end()) break; + os << " Node(" << it->first << ")["; + if (!it->second.empty()) { + for (auto edge : it->second) { + os << "Edge(" << "->" << edge.first << ",cost:" << edge.second << ")"; + os << ","; + } + } + os << "]" << std::endl; + } + + os << "]"; + + return os.str(); + } + + + friend std::ostream& operator<<(std::ostream& strm, const Graph& g) { + return strm << g.toString(); + } + + }; } // namespace dsa