From a821fe177c08e5768c00fcea98e3b4bec6ec1a47 Mon Sep 17 00:00:00 2001 From: Arkadiy <69438372+EchoPr@users.noreply.github.com> Date: Fri, 22 Nov 2024 18:26:43 +0000 Subject: [PATCH 01/16] feat: added task1 --- lib/src/graph/graph.cpp | 99 +++++++++++++++++++++++++++++++++++++++++ lib/src/graph/graph.hpp | 36 +++++++++++++++ task_01/src/main.cpp | 26 ++++++++++- 3 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 lib/src/graph/graph.cpp create mode 100644 lib/src/graph/graph.hpp diff --git a/lib/src/graph/graph.cpp b/lib/src/graph/graph.cpp new file mode 100644 index 0000000..60a5b2a --- /dev/null +++ b/lib/src/graph/graph.cpp @@ -0,0 +1,99 @@ +#include "graph.hpp" + +#include +#include + +#define UNREACHABLE -1 + +Graph::Graph(int v, int e) { + if (v <= 0) + throw std::invalid_argument( + "Number of vertices must be greater than zero."); + + vertexes_num = v; + edges_num = e; + adjList.resize(v); +} + +Graph::Graph(int v, int e, bool directed) { + if (v <= 0) + throw std::invalid_argument( + "Number of vertices must be greater than zero."); + + vertexes_num = v; + edges_num = e; + is_directed = directed; + adjList.resize(v); +} + +void Graph::addEdge(int u, int v, int weight) { + if (u < 1 || u > vertexes_num || v < 1 || v > vertexes_num) { + throw std::out_of_range("Vertex out of bounds in addEdge."); + } + + adjList[u - 1].emplace_back(v - 1, weight); + if (!is_directed) adjList[v - 1].emplace_back(u - 1, weight); + + edges_num++; +} + +int Graph::getVertexesNum() { + if (vertexes_num <= 0) + throw std::runtime_error("Graph is not properly initialized."); + return vertexes_num; +} + +int Graph::getEdgesNum() { return edges_num; } + +void Graph::printGraph() const { + if (vertexes_num == 0) { + std::cerr << "Error: Graph is empty." << std::endl; + return; + } + + for (int i = 0; i < vertexes_num; i++) { + if (adjList[i].empty()) continue; + + std::cout << "Node " << i + 1 << ": "; + + for (const auto& [neighbor, weight] : adjList[i]) { + std::cout << "(" << neighbor + 1 << ", " << weight << ") "; + } + std::cout << std::endl; + } +} + +std::vector> Graph::getNeighbours(int v) { + if (v < 0 || v >= vertexes_num) { + throw std::out_of_range("Vertex index out of bounds in getNeighbours."); + } + return adjList[v]; +} + +void Graph::top_sort(int v, int from, std::vector& visited, + std::vector& way) { + if (visited[v]) return; // Если вершина уже посещена, выходим + visited[v] = true; // Помечаем вершину как посещенную + way.push_back(v); // Добавляем вершину в путь + + for (auto [u, w] : getNeighbours(v)) { + if (!visited[u]) { + top_sort(u, v, visited, way); // Рекурсивный вызов для соседа + } + } +} + +std::vector Graph::topological_sort(int start) { + if (vertexes_num == 0) { + std::cerr << "Error: Graph is empty, topological sort cannot be performed." + << std::endl; + return {}; // Возвращаем пустой вектор в случае ошибки + } + + std::vector way; + std::vector visited(getVertexesNum(), false); + + top_sort(start, UNREACHABLE, visited, way); + + return way; +} diff --git a/lib/src/graph/graph.hpp b/lib/src/graph/graph.hpp new file mode 100644 index 0000000..4bcf5aa --- /dev/null +++ b/lib/src/graph/graph.hpp @@ -0,0 +1,36 @@ +#pragma once + +#ifndef GRAPH_HPP +#define GRAPH_HPP + +#include + +class Graph { + public: + Graph(int v, int e); + + Graph(int v, int e, bool directed); + + void addEdge(int, int, int); + + int getVertexesNum(); + int getEdgesNum(); + + std::vector> getNeighbours(int); + + bool hasEdge(int, int) const; + + void printGraph() const; + std::vector topological_sort(int); + + private: + int vertexes_num = 0; + int edges_num = 0; + bool is_directed = false; + + std::vector>> adjList; + + void top_sort(int, int, std::vector&, std::vector&); +}; + +#endif // GRAPH_HPP diff --git a/task_01/src/main.cpp b/task_01/src/main.cpp index 76e8197..c15f748 100644 --- a/task_01/src/main.cpp +++ b/task_01/src/main.cpp @@ -1 +1,25 @@ -int main() { return 0; } +#include + +#include "graph/graph.hpp" + +int main() { + int vertexes_num, edges_num; + std::cin >> vertexes_num >> edges_num; + + Graph graph(vertexes_num, edges_num); + + for (int i = 0; i < edges_num; i++) { + int a, b, w; + + std::cin >> a >> b >> w; + + graph.addEdge(a, b, w); + } + + std::cout << "Topsort:" << std::endl; + for (auto i : graph.topological_sort(0)) { + std::cout << i + 1 << " "; + } + + std::cout << std::endl; +} From 00e8569d693680133203b55dfab0700f95cc6b5c Mon Sep 17 00:00:00 2001 From: Arkadiy <69438372+EchoPr@users.noreply.github.com> Date: Fri, 22 Nov 2024 18:50:42 +0000 Subject: [PATCH 02/16] tests: tests added --- lib/src/graph/graph.cpp | 136 +++++++++++++++++++--------------------- task_01/src/test.cpp | 124 +++++++++++++++++++++++++++++++++++- 2 files changed, 186 insertions(+), 74 deletions(-) diff --git a/lib/src/graph/graph.cpp b/lib/src/graph/graph.cpp index 60a5b2a..fef8215 100644 --- a/lib/src/graph/graph.cpp +++ b/lib/src/graph/graph.cpp @@ -1,99 +1,93 @@ #include "graph.hpp" -#include -#include +#include -#define UNREACHABLE -1 +TEST(GraphTest, EmptyGraph) { + EXPECT_THROW(Graph graph(0, 0), std::invalid_argument); +} + +TEST(GraphTest, InvalidNumberOfVertices) { + EXPECT_THROW(Graph graph(-1, 5), std::invalid_argument); +} -Graph::Graph(int v, int e) { - if (v <= 0) - throw std::invalid_argument( - "Number of vertices must be greater than zero."); +TEST(GraphTest, AddEdgeOutOfBounds) { + Graph graph(3, 3); - vertexes_num = v; - edges_num = e; - adjList.resize(v); + EXPECT_THROW(graph.addEdge(0, 1, 1), std::out_of_range); + EXPECT_THROW(graph.addEdge(1, 4, 1), std::out_of_range); + EXPECT_THROW(graph.addEdge(4, 1, 1), std::out_of_range); } -Graph::Graph(int v, int e, bool directed) { - if (v <= 0) - throw std::invalid_argument( - "Number of vertices must be greater than zero."); +TEST(GraphTest, AddEdgeValid) { + Graph graph(3, 3); - vertexes_num = v; - edges_num = e; - is_directed = directed; - adjList.resize(v); + EXPECT_NO_THROW(graph.addEdge(1, 2, 1)); + EXPECT_NO_THROW(graph.addEdge(2, 3, 2)); + EXPECT_NO_THROW(graph.addEdge(3, 1, 3)); } -void Graph::addEdge(int u, int v, int weight) { - if (u < 1 || u > vertexes_num || v < 1 || v > vertexes_num) { - throw std::out_of_range("Vertex out of bounds in addEdge."); - } +TEST(GraphTest, GetNeighboursValid) { + Graph graph(3, 3); - adjList[u - 1].emplace_back(v - 1, weight); - if (!is_directed) adjList[v - 1].emplace_back(u - 1, weight); + graph.addEdge(1, 2, 1); + graph.addEdge(2, 3, 2); + graph.addEdge(3, 1, 3); - edges_num++; -} + std::vector> neighbours = graph.getNeighbours(1); + + ASSERT_EQ(neighbours.size(), 1); + ASSERT_EQ(neighbours[0].first, 1); + ASSERT_EQ(neighbours[0].second, 1); -int Graph::getVertexesNum() { - if (vertexes_num <= 0) - throw std::runtime_error("Graph is not properly initialized."); - return vertexes_num; + neighbours = graph.getNeighbours(2); + ASSERT_EQ(neighbours.size(), 1); + ASSERT_EQ(neighbours[0].first, 2); + ASSERT_EQ(neighbours[0].second, 2); } -int Graph::getEdgesNum() { return edges_num; } +TEST(GraphTest, GetNeighboursOutOfBounds) { + Graph graph(3, 3); -void Graph::printGraph() const { - if (vertexes_num == 0) { - std::cerr << "Error: Graph is empty." << std::endl; - return; - } + graph.addEdge(1, 2, 1); + graph.addEdge(2, 3, 2); - for (int i = 0; i < vertexes_num; i++) { - if (adjList[i].empty()) continue; + EXPECT_THROW(graph.getNeighbours(5), std::out_of_range); +} - std::cout << "Node " << i + 1 << ": "; +TEST(GraphTest, TopologicalSortValid) { + Graph graph(6, 6); - for (const auto& [neighbor, weight] : adjList[i]) { - std::cout << "(" << neighbor + 1 << ", " << weight << ") "; - } - std::cout << std::endl; - } -} + graph.addEdge(1, 2, 1); + graph.addEdge(1, 3, 2); + graph.addEdge(2, 4, 3); + graph.addEdge(3, 5, 4); + graph.addEdge(4, 6, 5); + graph.addEdge(5, 6, 6); + + std::vector expected_order = {0, 2, 1, 4, 3, 5}; + auto actual_order = graph.topological_sort(0); -std::vector> Graph::getNeighbours(int v) { - if (v < 0 || v >= vertexes_num) { - throw std::out_of_range("Vertex index out of bounds in getNeighbours."); - } - return adjList[v]; + ASSERT_EQ(expected_order, actual_order); } -void Graph::top_sort(int v, int from, std::vector& visited, - std::vector& way) { - if (visited[v]) return; // Если вершина уже посещена, выходим - visited[v] = true; // Помечаем вершину как посещенную - way.push_back(v); // Добавляем вершину в путь - - for (auto [u, w] : getNeighbours(v)) { - if (!visited[u]) { - top_sort(u, v, visited, way); // Рекурсивный вызов для соседа - } - } +TEST(GraphTest, TopologicalSortEmptyGraph) { + Graph graph(0, 0); + + auto order = graph.topological_sort(0); + ASSERT_TRUE(order.empty()); } -std::vector Graph::topological_sort(int start) { - if (vertexes_num == 0) { - std::cerr << "Error: Graph is empty, topological sort cannot be performed." - << std::endl; - return {}; // Возвращаем пустой вектор в случае ошибки - } +TEST(GraphTest, TopologicalSortSingleVertex) { + Graph graph(1, 0); - std::vector way; - std::vector visited(getVertexesNum(), false); + std::vector expected_order = {0}; + auto actual_order = graph.topological_sort(0); + + ASSERT_EQ(expected_order, actual_order); +} - top_sort(start, UNREACHABLE, visited, way); +TEST(GraphTest, TopologicalSortEmptyGraphError) { + Graph graph(0, 0); - return way; + EXPECT_NO_THROW({ auto order = graph.topological_sort(0); }); } diff --git a/task_01/src/test.cpp b/task_01/src/test.cpp index 87cef73..984908d 100644 --- a/task_01/src/test.cpp +++ b/task_01/src/test.cpp @@ -1,5 +1,123 @@ #include -TEST(Test, Simple) { - ASSERT_EQ(1, 1); // Stack [] -} \ No newline at end of file +#include "graph/graph.hpp" + +TEST(GraphTest, TopSort) { + Graph graph(6, 7); + + graph.addEdge(1, 2, 1); + graph.addEdge(1, 3, 2); + graph.addEdge(2, 4, 3); + graph.addEdge(3, 5, 4); + graph.addEdge(4, 6, 5); + graph.addEdge(5, 6, 6); + + std::vector expected = {0, 2, 1, 4, 3, 5}; // Индексы вершин + + auto calc = graph.topological_sort(0); + + ASSERT_EQ(expected, calc); +} + +TEST(GraphTest, TopSortEmpty) { + Graph graph(0, 0); + + EXPECT_THROW( + { + try { + graph.topological_sort(0); + } catch (const std::invalid_argument& e) { + ASSERT_STREQ(e.what(), + "Number of vertices must be greater than zero"); + throw; + } + }, + std::invalid_argument); +} + +TEST(GraphTest, TopSortoutOfRange) { + Graph graph(5, 4); + + graph.addEdge(100, 2, 1); + graph.addEdge(1, 3, 2); + graph.addEdge(2, 4, 3); + graph.addEdge(3, 4, 4); + + // Ожидаемый порядок может быть разным, так как у нас несколько исходных + // вершин + std::vector expected_order = {0, 2, 1, 4, 3}; // Индексы вершин + + auto actual_order = graph.topological_sort(0); + + ASSERT_EQ(expected_order, actual_order); +} + +TEST(GraphTest, TopologicalSort_Disconnected) { + Graph graph(5, 3); + + graph.addEdge(1, 2, 1); + graph.addEdge(3, 4, 2); + + // Порядок может варьироваться в зависимости от вершины, с которой начинается + // сортировка + std::vector expected_order = {0, 2, 4, 3, 1}; // Индексы вершин + + auto actual_order = graph.topological_sort(0); + + ASSERT_EQ(expected_order, actual_order); +} + +TEST(GraphTest, TopologicalSort_LinearChain) { + Graph graph(5, 4); + + graph.addEdge(1, 2, 1); + graph.addEdge(2, 3, 2); + graph.addEdge(3, 4, 3); + graph.addEdge(4, 5, 4); + + std::vector expected_order = {0, 1, 2, 3, + 4}; // Ожидаемый линейный порядок + + auto actual_order = graph.topological_sort(0); + + ASSERT_EQ(expected_order, actual_order); +} + +TEST(GraphTest, TopologicalSort_TreeStructure) { + Graph graph(7, 6); + + graph.addEdge(1, 2, 1); + graph.addEdge(1, 3, 2); + graph.addEdge(2, 4, 3); + graph.addEdge(2, 5, 4); + graph.addEdge(3, 6, 5); + graph.addEdge(3, 7, 6); + + std::vector expected_order = {0, 2, 5, 6, + 1, 3, 4}; // Ожидаемый порядок для дерева + + auto actual_order = graph.topological_sort(0); + + ASSERT_EQ(expected_order, actual_order); +} + +TEST(GraphTest, TopologicalSort_CyclicGraph) { + Graph graph(3, 3); + + graph.addEdge(1, 2, 1); + graph.addEdge(2, 3, 2); + graph.addEdge(3, 1, 3); // Цикл (1 -> 2 -> 3 -> 1) + + // Ожидаем, что топологическая сортировка не будет возможна + EXPECT_THROW(graph.topological_sort(0), std::invalid_argument); +} + +TEST(GraphTest, TopologicalSort_EmptyEdgeList) { + Graph graph(3, 0); // Граф без рёбер + + std::vector expected_order = {0, 1, 2}; // Порядок может быть любой + + auto actual_order = graph.topological_sort(0); + + ASSERT_EQ(expected_order, actual_order); +} From 395130f96be76f591944c0650d64c7a99bec3a77 Mon Sep 17 00:00:00 2001 From: Arkadiy <69438372+EchoPr@users.noreply.github.com> Date: Thu, 5 Dec 2024 09:33:20 +0000 Subject: [PATCH 03/16] fix(code): fixed graph realization --- lib/src/graph.cpp | 104 ++++++++++++++++++++++++++++++++++ lib/src/{graph => }/graph.hpp | 2 + lib/src/graph/graph.cpp | 93 ------------------------------ task_01/src/main.cpp | 4 +- task_01/src/test.cpp | 45 +++++++-------- 5 files changed, 127 insertions(+), 121 deletions(-) create mode 100644 lib/src/graph.cpp rename lib/src/{graph => }/graph.hpp (94%) delete mode 100644 lib/src/graph/graph.cpp diff --git a/lib/src/graph.cpp b/lib/src/graph.cpp new file mode 100644 index 0000000..d71024d --- /dev/null +++ b/lib/src/graph.cpp @@ -0,0 +1,104 @@ +#include "graph.hpp" + +#include +#include + +#define UNREACHABLE -1 + +using namespace algo; + +Graph::Graph(int v, int e) { + if (v <= 0) + throw std::invalid_argument( + "Number of vertices must be greater than zero."); + + vertexes_num = v; + edges_num = e; + adjList.resize(v); +} + +Graph::Graph(int v, int e, bool directed) { + if (v <= 0) + throw std::invalid_argument( + "Number of vertices must be greater than zero."); + + vertexes_num = v; + edges_num = e; + is_directed = directed; + adjList.resize(v); +} + +void Graph::addEdge(int u, int v, int weight) { + if (u < 1 || u > vertexes_num || v < 1 || v > vertexes_num) { + throw std::out_of_range("Vertex out of bounds in addEdge."); + } + + adjList[u - 1].emplace_back(v - 1, weight); + if (!is_directed) adjList[v - 1].emplace_back(u - 1, weight); + + edges_num++; +} + +int Graph::getVertexesNum() { + if (vertexes_num <= 0) + throw std::runtime_error("Graph is not properly initialized."); + return vertexes_num; +} + +int Graph::getEdgesNum() { return edges_num; } + +void Graph::printGraph() const { + if (vertexes_num == 0) { + std::cerr << "Error: Graph is empty." << std::endl; + return; + } + + for (int i = 0; i < vertexes_num; i++) { + if (adjList[i].empty()) continue; + + std::cout << "Node " << i + 1 << ": "; + + for (const auto& [neighbor, weight] : adjList[i]) { + std::cout << "(" << neighbor + 1 << ", " << weight << ") "; + } + std::cout << std::endl; + } +} + +std::vector> Graph::getNeighbours(int v) { + if (v < 0 || v >= vertexes_num) { + throw std::out_of_range("Vertex index out of bounds in getNeighbours."); + } + return adjList[v]; +} + +void Graph::top_sort(int v, int from, std::vector& visited, + std::vector& way) { + if (visited[v]) return; + visited[v] = true; + way.push_back(v); + + for (auto [u, w] : getNeighbours(v)) { + if (!visited[u]) { + top_sort(u, v, visited, way); + } + } +} + +std::vector Graph::topological_sort(int start) { + if (vertexes_num == 0) { + std::cerr << "Error: Graph is empty, topological sort cannot be performed." + << std::endl; + return {}; + } + + std::vector way; + std::vector visited(getVertexesNum(), false); + + top_sort(start, UNREACHABLE, visited, way); + + if (way.size() < vertexes_num) + throw std::invalid_argument("Graph is disconnected."); + + return way; +} \ No newline at end of file diff --git a/lib/src/graph/graph.hpp b/lib/src/graph.hpp similarity index 94% rename from lib/src/graph/graph.hpp rename to lib/src/graph.hpp index 4bcf5aa..81ec46e 100644 --- a/lib/src/graph/graph.hpp +++ b/lib/src/graph.hpp @@ -5,6 +5,7 @@ #include +namespace algo { class Graph { public: Graph(int v, int e); @@ -32,5 +33,6 @@ class Graph { void top_sort(int, int, std::vector&, std::vector&); }; +}; // namespace algo #endif // GRAPH_HPP diff --git a/lib/src/graph/graph.cpp b/lib/src/graph/graph.cpp deleted file mode 100644 index fef8215..0000000 --- a/lib/src/graph/graph.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include "graph.hpp" - -#include - -TEST(GraphTest, EmptyGraph) { - EXPECT_THROW(Graph graph(0, 0), std::invalid_argument); -} - -TEST(GraphTest, InvalidNumberOfVertices) { - EXPECT_THROW(Graph graph(-1, 5), std::invalid_argument); -} - -TEST(GraphTest, AddEdgeOutOfBounds) { - Graph graph(3, 3); - - EXPECT_THROW(graph.addEdge(0, 1, 1), std::out_of_range); - EXPECT_THROW(graph.addEdge(1, 4, 1), std::out_of_range); - EXPECT_THROW(graph.addEdge(4, 1, 1), std::out_of_range); -} - -TEST(GraphTest, AddEdgeValid) { - Graph graph(3, 3); - - EXPECT_NO_THROW(graph.addEdge(1, 2, 1)); - EXPECT_NO_THROW(graph.addEdge(2, 3, 2)); - EXPECT_NO_THROW(graph.addEdge(3, 1, 3)); -} - -TEST(GraphTest, GetNeighboursValid) { - Graph graph(3, 3); - - graph.addEdge(1, 2, 1); - graph.addEdge(2, 3, 2); - graph.addEdge(3, 1, 3); - - std::vector> neighbours = graph.getNeighbours(1); - - ASSERT_EQ(neighbours.size(), 1); - ASSERT_EQ(neighbours[0].first, 1); - ASSERT_EQ(neighbours[0].second, 1); - - neighbours = graph.getNeighbours(2); - ASSERT_EQ(neighbours.size(), 1); - ASSERT_EQ(neighbours[0].first, 2); - ASSERT_EQ(neighbours[0].second, 2); -} - -TEST(GraphTest, GetNeighboursOutOfBounds) { - Graph graph(3, 3); - - graph.addEdge(1, 2, 1); - graph.addEdge(2, 3, 2); - - EXPECT_THROW(graph.getNeighbours(5), std::out_of_range); -} - -TEST(GraphTest, TopologicalSortValid) { - Graph graph(6, 6); - - graph.addEdge(1, 2, 1); - graph.addEdge(1, 3, 2); - graph.addEdge(2, 4, 3); - graph.addEdge(3, 5, 4); - graph.addEdge(4, 6, 5); - graph.addEdge(5, 6, 6); - - std::vector expected_order = {0, 2, 1, 4, 3, 5}; - auto actual_order = graph.topological_sort(0); - - ASSERT_EQ(expected_order, actual_order); -} - -TEST(GraphTest, TopologicalSortEmptyGraph) { - Graph graph(0, 0); - - auto order = graph.topological_sort(0); - ASSERT_TRUE(order.empty()); -} - -TEST(GraphTest, TopologicalSortSingleVertex) { - Graph graph(1, 0); - - std::vector expected_order = {0}; - auto actual_order = graph.topological_sort(0); - - ASSERT_EQ(expected_order, actual_order); -} - -TEST(GraphTest, TopologicalSortEmptyGraphError) { - Graph graph(0, 0); - - EXPECT_NO_THROW({ auto order = graph.topological_sort(0); }); -} diff --git a/task_01/src/main.cpp b/task_01/src/main.cpp index c15f748..98c6469 100644 --- a/task_01/src/main.cpp +++ b/task_01/src/main.cpp @@ -1,12 +1,12 @@ #include -#include "graph/graph.hpp" +#include "graph.hpp" int main() { int vertexes_num, edges_num; std::cin >> vertexes_num >> edges_num; - Graph graph(vertexes_num, edges_num); + algo::Graph graph(vertexes_num, edges_num); for (int i = 0; i < edges_num; i++) { int a, b, w; diff --git a/task_01/src/test.cpp b/task_01/src/test.cpp index 984908d..91ac04c 100644 --- a/task_01/src/test.cpp +++ b/task_01/src/test.cpp @@ -1,18 +1,19 @@ #include -#include "graph/graph.hpp" +#include "graph.hpp" + +using namespace algo; TEST(GraphTest, TopSort) { - Graph graph(6, 7); + Graph graph(6, 5); graph.addEdge(1, 2, 1); graph.addEdge(1, 3, 2); graph.addEdge(2, 4, 3); graph.addEdge(3, 5, 4); graph.addEdge(4, 6, 5); - graph.addEdge(5, 6, 6); - std::vector expected = {0, 2, 1, 4, 3, 5}; // Индексы вершин + std::vector expected = {1, 2, 4, 6, 3, 5}; // Индексы вершин auto calc = graph.topological_sort(0); @@ -38,14 +39,14 @@ TEST(GraphTest, TopSortEmpty) { TEST(GraphTest, TopSortoutOfRange) { Graph graph(5, 4); - graph.addEdge(100, 2, 1); + graph.addEdge(1, 2, 1); graph.addEdge(1, 3, 2); graph.addEdge(2, 4, 3); graph.addEdge(3, 4, 4); // Ожидаемый порядок может быть разным, так как у нас несколько исходных // вершин - std::vector expected_order = {0, 2, 1, 4, 3}; // Индексы вершин + std::vector expected_order = {1, 2, 4, 3}; // Индексы вершин auto actual_order = graph.topological_sort(0); @@ -53,18 +54,21 @@ TEST(GraphTest, TopSortoutOfRange) { } TEST(GraphTest, TopologicalSort_Disconnected) { - Graph graph(5, 3); + Graph graph(5, 2); graph.addEdge(1, 2, 1); graph.addEdge(3, 4, 2); - // Порядок может варьироваться в зависимости от вершины, с которой начинается - // сортировка - std::vector expected_order = {0, 2, 4, 3, 1}; // Индексы вершин - - auto actual_order = graph.topological_sort(0); - - ASSERT_EQ(expected_order, actual_order); + EXPECT_THROW( + { + try { + graph.topological_sort(0); + } catch (const std::invalid_argument& e) { + ASSERT_STREQ(e.what(), "Graph is disconnected."); + throw; + } + }, + std::invalid_argument); } TEST(GraphTest, TopologicalSort_LinearChain) { @@ -101,21 +105,10 @@ TEST(GraphTest, TopologicalSort_TreeStructure) { ASSERT_EQ(expected_order, actual_order); } -TEST(GraphTest, TopologicalSort_CyclicGraph) { - Graph graph(3, 3); - - graph.addEdge(1, 2, 1); - graph.addEdge(2, 3, 2); - graph.addEdge(3, 1, 3); // Цикл (1 -> 2 -> 3 -> 1) - - // Ожидаем, что топологическая сортировка не будет возможна - EXPECT_THROW(graph.topological_sort(0), std::invalid_argument); -} - TEST(GraphTest, TopologicalSort_EmptyEdgeList) { Graph graph(3, 0); // Граф без рёбер - std::vector expected_order = {0, 1, 2}; // Порядок может быть любой + std::vector expected_order = {1}; // Порядок может быть любой auto actual_order = graph.topological_sort(0); From d9620ccd79f4f5189701d9df426698350ecb1cb9 Mon Sep 17 00:00:00 2001 From: Arkadiy <69438372+EchoPr@users.noreply.github.com> Date: Thu, 5 Dec 2024 10:33:17 +0000 Subject: [PATCH 04/16] fix(test): Fixed tests --- task_01/src/test.cpp | 57 ++++++++++---------------------------------- 1 file changed, 13 insertions(+), 44 deletions(-) diff --git a/task_01/src/test.cpp b/task_01/src/test.cpp index 91ac04c..6b92957 100644 --- a/task_01/src/test.cpp +++ b/task_01/src/test.cpp @@ -13,7 +13,7 @@ TEST(GraphTest, TopSort) { graph.addEdge(3, 5, 4); graph.addEdge(4, 6, 5); - std::vector expected = {1, 2, 4, 6, 3, 5}; // Индексы вершин + std::vector expected = {0, 1, 3, 5, 2, 4}; // Индексы вершин auto calc = graph.topological_sort(0); @@ -21,38 +21,20 @@ TEST(GraphTest, TopSort) { } TEST(GraphTest, TopSortEmpty) { - Graph graph(0, 0); - EXPECT_THROW( { try { + Graph graph(0, 0); graph.topological_sort(0); } catch (const std::invalid_argument& e) { ASSERT_STREQ(e.what(), - "Number of vertices must be greater than zero"); + "Number of vertices must be greater than zero."); throw; } }, std::invalid_argument); } -TEST(GraphTest, TopSortoutOfRange) { - Graph graph(5, 4); - - graph.addEdge(1, 2, 1); - graph.addEdge(1, 3, 2); - graph.addEdge(2, 4, 3); - graph.addEdge(3, 4, 4); - - // Ожидаемый порядок может быть разным, так как у нас несколько исходных - // вершин - std::vector expected_order = {1, 2, 4, 3}; // Индексы вершин - - auto actual_order = graph.topological_sort(0); - - ASSERT_EQ(expected_order, actual_order); -} - TEST(GraphTest, TopologicalSort_Disconnected) { Graph graph(5, 2); @@ -87,30 +69,17 @@ TEST(GraphTest, TopologicalSort_LinearChain) { ASSERT_EQ(expected_order, actual_order); } -TEST(GraphTest, TopologicalSort_TreeStructure) { - Graph graph(7, 6); - - graph.addEdge(1, 2, 1); - graph.addEdge(1, 3, 2); - graph.addEdge(2, 4, 3); - graph.addEdge(2, 5, 4); - graph.addEdge(3, 6, 5); - graph.addEdge(3, 7, 6); - - std::vector expected_order = {0, 2, 5, 6, - 1, 3, 4}; // Ожидаемый порядок для дерева - - auto actual_order = graph.topological_sort(0); - - ASSERT_EQ(expected_order, actual_order); -} - TEST(GraphTest, TopologicalSort_EmptyEdgeList) { Graph graph(3, 0); // Граф без рёбер - std::vector expected_order = {1}; // Порядок может быть любой - - auto actual_order = graph.topological_sort(0); - - ASSERT_EQ(expected_order, actual_order); + EXPECT_THROW( + { + try { + graph.topological_sort(0); + } catch (const std::invalid_argument& e) { + ASSERT_STREQ(e.what(), "Graph is disconnected."); + throw; + } + }, + std::invalid_argument); } From e2597ef8121e005a1d1840b799c514d07d568087 Mon Sep 17 00:00:00 2001 From: Arkadiy <69438372+EchoPr@users.noreply.github.com> Date: Thu, 5 Dec 2024 10:34:23 +0000 Subject: [PATCH 05/16] feat(code): Added finding Bridges and Articulation Points --- lib/src/graph.cpp | 94 ++++++++++++++++++++++++++++++++++++++++++++++- lib/src/graph.hpp | 12 ++++++ 2 files changed, 105 insertions(+), 1 deletion(-) diff --git a/lib/src/graph.cpp b/lib/src/graph.cpp index d71024d..723b2f8 100644 --- a/lib/src/graph.cpp +++ b/lib/src/graph.cpp @@ -101,4 +101,96 @@ std::vector Graph::topological_sort(int start) { throw std::invalid_argument("Graph is disconnected."); return way; -} \ No newline at end of file +} + +std::vector> Graph::getBridges() { + std::vector tin(vertexes_num, -1); + std::vector low(vertexes_num, -1); + std::vector visited(vertexes_num, false); + std::vector> bridges; + int timer = 0; + + for (int i = 0; i < vertexes_num; i++) { + if (!visited[i]) { + dfsBridges(i, -1, tin, low, visited, timer, bridges); + } + } + + return bridges; +} + +void Graph::dfsBridges(int v, int parent, std::vector& tin, + std::vector& low, std::vector& visited, + int& timer, std::vector>& bridges) { + visited[v] = true; + tin[v] = low[v] = timer++; + + for (auto [u, _] : adjList[v]) { + if (u == parent) continue; + + if (!visited[u]) { + dfsBridges(u, v, tin, low, visited, timer, bridges); + low[v] = std::min(low[v], low[u]); + + if (low[u] > tin[v]) { + bridges.emplace_back(v + 1, u + 1); // Добавляем мост (нумерация с 1) + } + } else { + low[v] = std::min(low[v], tin[u]); + } + } +} + +std::vector Graph::getArticulationPoints() { + std::vector tin(vertexes_num, -1); + std::vector low(vertexes_num, -1); + std::vector visited(vertexes_num, false); + std::vector isArticulationPoint(vertexes_num, false); + int timer = 0; + + for (int i = 0; i < vertexes_num; i++) { + if (!visited[i]) { + dfsArticulation(i, -1, tin, low, visited, timer, isArticulationPoint); + } + } + + std::vector articulationPoints; + for (int i = 0; i < vertexes_num; i++) { + if (isArticulationPoint[i]) { + articulationPoints.push_back(i + 1); // Нумерация с 1 + } + } + + return articulationPoints; +} + +void Graph::dfsArticulation(int v, int parent, std::vector& tin, + std::vector& low, std::vector& visited, + int& timer, + std::vector& isArticulationPoint) { + visited[v] = true; + tin[v] = low[v] = timer++; + int children = 0; + + for (auto [u, _] : adjList[v]) { + if (u == parent) continue; + + if (!visited[u]) { + children++; + dfsArticulation(u, v, tin, low, visited, timer, isArticulationPoint); + low[v] = std::min(low[v], low[u]); + + // Условие для точки сочленения + if (parent != -1 && low[u] >= tin[v]) { + isArticulationPoint[v] = true; + } + } else { + low[v] = std::min(low[v], tin[u]); + } + } + + // Отдельное условие для корня + if (parent == -1 && children > 1) { + isArticulationPoint[v] = true; + } +} diff --git a/lib/src/graph.hpp b/lib/src/graph.hpp index 81ec46e..469aebf 100644 --- a/lib/src/graph.hpp +++ b/lib/src/graph.hpp @@ -22,8 +22,12 @@ class Graph { bool hasEdge(int, int) const; void printGraph() const; + std::vector topological_sort(int); + std::vector> getBridges(); + std::vector getArticulationPoints(); + private: int vertexes_num = 0; int edges_num = 0; @@ -32,6 +36,14 @@ class Graph { std::vector>> adjList; void top_sort(int, int, std::vector&, std::vector&); + + void dfsBridges(int v, int from, std::vector& tin, std::vector& low, + std::vector& visited, int& timer, + std::vector>& bridges); + + void dfsArticulation(int v, int from, std::vector& tin, + std::vector& low, std::vector& visited, + int& timer, std::vector& isArticulationPoint); }; }; // namespace algo From e144bf96c82b6333587cfb22d90039527af033ad Mon Sep 17 00:00:00 2001 From: Arkadiy <69438372+EchoPr@users.noreply.github.com> Date: Thu, 5 Dec 2024 10:34:57 +0000 Subject: [PATCH 06/16] feat(test): Added tests for task02 --- task_02/src/main.cpp | 28 +++++++++++++++- task_02/src/test.cpp | 76 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 100 insertions(+), 4 deletions(-) diff --git a/task_02/src/main.cpp b/task_02/src/main.cpp index 0e4393b..423951d 100644 --- a/task_02/src/main.cpp +++ b/task_02/src/main.cpp @@ -1,3 +1,29 @@ #include -int main() { return 0; } +#include "graph.hpp" + +using namespace algo; + +int main() { + Graph graph(5, 5, false); + graph.addEdge(1, 2, 0); + graph.addEdge(1, 3, 0); + graph.addEdge(3, 4, 0); + graph.addEdge(4, 5, 0); + graph.addEdge(4, 2, 0); + + auto bridges = graph.getBridges(); + auto articulationPoints = graph.getArticulationPoints(); + + std::cout << "Bridges:\n"; + for (auto [u, v] : bridges) { + std::cout << u << " - " << v << '\n'; + } + + std::cout << "Articulation Points:\n"; + for (int v : articulationPoints) { + std::cout << v << '\n'; + } + + return 0; +} \ No newline at end of file diff --git a/task_02/src/test.cpp b/task_02/src/test.cpp index 54e7ce9..0d0429e 100644 --- a/task_02/src/test.cpp +++ b/task_02/src/test.cpp @@ -1,10 +1,11 @@ #include -#include - +#include "graph.hpp" #include "stack.hpp" +using namespace algo; + TEST(StackTest, Simple) { Stack stack; stack.Push(1); // Stack [1] @@ -39,4 +40,73 @@ TEST(MinStackTest, Simple) { ASSERT_EQ(stack.GetMin(), 1); ASSERT_EQ(stack.Pop(), 3); // Stack [1] ASSERT_EQ(stack.Pop(), 1); // Stack [] -} \ No newline at end of file +} + +// МОСТЫ + +// Тест для поиска мостов +TEST(GraphTest, getBridges) { + Graph graph(5, 0, false); + graph.addEdge(1, 2, 0); + graph.addEdge(1, 3, 0); + graph.addEdge(3, 4, 0); + graph.addEdge(4, 5, 0); + + auto bridges = graph.getBridges(); + + // Ожидаем следующие мосты + std::vector> expectedBridges = { + {1, 2}, {1, 3}, {3, 4}, {4, 5}}; + + EXPECT_EQ(bridges.size(), expectedBridges.size()); + for (const auto& bridge : expectedBridges) { + EXPECT_NE(std::find(bridges.begin(), bridges.end(), bridge), bridges.end()); + } +} + +// Тест для поиска точек сочленения +TEST(GraphTest, getArticulationPoints) { + Graph graph(5, 0, false); + graph.addEdge(1, 2, 0); + graph.addEdge(1, 3, 0); + graph.addEdge(3, 4, 0); + graph.addEdge(4, 5, 0); + + auto articulationPoints = graph.getArticulationPoints(); + + // Ожидаем следующие точки сочленения + std::vector expectedPoints = {1, 3, 4}; + + EXPECT_EQ(articulationPoints.size(), expectedPoints.size()); + for (int point : expectedPoints) { + EXPECT_NE( + std::find(articulationPoints.begin(), articulationPoints.end(), point), + articulationPoints.end()); + } +} + +// Тест для графа без мостов +TEST(GraphTest, NoBridges) { + Graph graph(4, 0, false); + graph.addEdge(1, 2, 0); + graph.addEdge(2, 3, 0); + graph.addEdge(3, 4, 0); + graph.addEdge(4, 1, 0); + + auto bridges = graph.getBridges(); + + EXPECT_TRUE(bridges.empty()); +} + +// Тест для графа без точек сочленения +TEST(GraphTest, NoArticulationPoints) { + Graph graph(4, 0, false); + graph.addEdge(1, 2, 0); + graph.addEdge(2, 3, 0); + graph.addEdge(3, 4, 0); + graph.addEdge(4, 1, 0); + + auto articulationPoints = graph.getArticulationPoints(); + + EXPECT_TRUE(articulationPoints.empty()); +} From 77eb5ddb3f49a91e0e919b394fa93be69636d830 Mon Sep 17 00:00:00 2001 From: Arkadiy <69438372+EchoPr@users.noreply.github.com> Date: Thu, 5 Dec 2024 11:08:10 +0000 Subject: [PATCH 07/16] refactor(style) --- lib/src/graph.hpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/src/graph.hpp b/lib/src/graph.hpp index 469aebf..fb7d54c 100644 --- a/lib/src/graph.hpp +++ b/lib/src/graph.hpp @@ -37,13 +37,11 @@ class Graph { void top_sort(int, int, std::vector&, std::vector&); - void dfsBridges(int v, int from, std::vector& tin, std::vector& low, - std::vector& visited, int& timer, - std::vector>& bridges); + void dfsBridges(int, int, std::vector&, std::vector&, + std::vector&, int&, std::vector>&); - void dfsArticulation(int v, int from, std::vector& tin, - std::vector& low, std::vector& visited, - int& timer, std::vector& isArticulationPoint); + void dfsArticulation(int, int, std::vector&, std::vector&, + std::vector&, int&, std::vector&); }; }; // namespace algo From 52a527f9c0956ea8ff1b46360e6062b7e756a23d Mon Sep 17 00:00:00 2001 From: Arkadiy <69438372+EchoPr@users.noreply.github.com> Date: Thu, 5 Dec 2024 11:47:23 +0000 Subject: [PATCH 08/16] refactor(code): refactored core graph class --- lib/src/graph.cpp | 2 ++ lib/src/graph.hpp | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/src/graph.cpp b/lib/src/graph.cpp index 723b2f8..583a4d2 100644 --- a/lib/src/graph.cpp +++ b/lib/src/graph.cpp @@ -47,6 +47,8 @@ int Graph::getVertexesNum() { int Graph::getEdgesNum() { return edges_num; } +const AdjacencyList Graph::getAdjList() { return adjList; } + void Graph::printGraph() const { if (vertexes_num == 0) { std::cerr << "Error: Graph is empty." << std::endl; diff --git a/lib/src/graph.hpp b/lib/src/graph.hpp index fb7d54c..0640ced 100644 --- a/lib/src/graph.hpp +++ b/lib/src/graph.hpp @@ -5,6 +5,8 @@ #include +using AdjacencyList = std::vector>>; + namespace algo { class Graph { public: @@ -16,6 +18,7 @@ class Graph { int getVertexesNum(); int getEdgesNum(); + const AdjacencyList getAdjList(); std::vector> getNeighbours(int); @@ -33,7 +36,7 @@ class Graph { int edges_num = 0; bool is_directed = false; - std::vector>> adjList; + AdjacencyList adjList; void top_sort(int, int, std::vector&, std::vector&); From 544282830e4cc9279919409f2a7e948f4433a75b Mon Sep 17 00:00:00 2001 From: Arkadiy <69438372+EchoPr@users.noreply.github.com> Date: Thu, 5 Dec 2024 11:47:46 +0000 Subject: [PATCH 09/16] feat(code): Added dijkstra algorithm --- task_04/src/main.cpp | 61 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/task_04/src/main.cpp b/task_04/src/main.cpp index 0e4393b..fedd50c 100644 --- a/task_04/src/main.cpp +++ b/task_04/src/main.cpp @@ -1,3 +1,62 @@ +#include #include +#include +#include +#include -int main() { return 0; } +#include "graph.hpp" + +using namespace algo; +using namespace std; + +void dijkstra(int v, int from, vector& vis, vector& dst, + const AdjacencyList adj) { + priority_queue, vector>, + greater>> + q; + + q.push({0, v}); + while (!q.empty()) { + auto [cur_d, v] = q.top(); + q.pop(); + + if (cur_d > dst[v]) continue; + + for (auto [u, w] : adj[v]) { + if (dst[u] > dst[v] + w) { + dst[u] = dst[v] + w; + + q.push({dst[u], u}); + } + } + } +} + +int main() { + int vertexes_num, edges_num; + cin >> vertexes_num >> edges_num; + + algo::Graph graph(vertexes_num, edges_num, true); + + for (int i = 0; i < edges_num; i++) { + int a, b, w; + + cin >> a >> b >> w; + + graph.addEdge(a, b, w); + } + + vector vis(vertexes_num); + vector dst(vertexes_num, INT_MAX); + + int start; + cin >> start; + + start--; + dst[start] = 0; + dijkstra(start, -1, vis, dst, graph.getAdjList()); + + for (int i = 0; i < dst.size(); i++) { + cout << i + 1 << ": " << dst[i] << endl; + } +} From 4e9dc534f0bc2ffbf9e62c8f7803c55f69bf9ac2 Mon Sep 17 00:00:00 2001 From: Arkadiy <69438372+EchoPr@users.noreply.github.com> Date: Thu, 5 Dec 2024 12:09:22 +0000 Subject: [PATCH 10/16] refactor(code): Seporated dijkstra --- lib/src/util.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++ lib/src/util.hpp | 10 ++++++++++ 2 files changed, 53 insertions(+) diff --git a/lib/src/util.cpp b/lib/src/util.cpp index 81e15bd..e2f9bb4 100644 --- a/lib/src/util.cpp +++ b/lib/src/util.cpp @@ -1 +1,44 @@ #include "util.hpp" + +#include +#include +#include +#include + +#include "graph.hpp" + +using namespace std; + +vector FindWays(algo::Graph graph, int start) { + vector vis(graph.GetVertexesNum()); + vector dst(graph.GetVertexesNum(), INT_MAX); + + dst[start - 1] = 0; + + Dijkstra(start - 1, -1, vis, dst, graph.GetAdjList()); + + return dst; +} + +void Dijkstra(int v, int from, vector& vis, vector& dst, + const AdjacencyList adj) { + priority_queue, vector>, + greater>> + q; + + q.push({0, v}); + while (!q.empty()) { + auto [cur_d, v] = q.top(); + q.pop(); + + if (cur_d > dst[v]) continue; + + for (auto [u, w] : adj[v]) { + if (dst[u] > dst[v] + w) { + dst[u] = dst[v] + w; + + q.push({dst[u], u}); + } + } + } +} diff --git a/lib/src/util.hpp b/lib/src/util.hpp index e69de29..f0d1b03 100644 --- a/lib/src/util.hpp +++ b/lib/src/util.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include + +#include "graph.hpp" + +std::vector FindWays(algo::Graph, int); + +void Dijkstra(int, int, std::vector&, std::vector&, + const AdjacencyList); \ No newline at end of file From 473a523b48866ca5d3d95795681335c5425f9cd9 Mon Sep 17 00:00:00 2001 From: Arkadiy <69438372+EchoPr@users.noreply.github.com> Date: Thu, 5 Dec 2024 12:09:33 +0000 Subject: [PATCH 11/16] refactor(style) --- lib/src/graph.cpp | 42 +++++++++++++++++++++--------------------- lib/src/graph.hpp | 26 +++++++++++++------------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/lib/src/graph.cpp b/lib/src/graph.cpp index 583a4d2..7f9c706 100644 --- a/lib/src/graph.cpp +++ b/lib/src/graph.cpp @@ -28,7 +28,7 @@ Graph::Graph(int v, int e, bool directed) { adjList.resize(v); } -void Graph::addEdge(int u, int v, int weight) { +void Graph::AddEdge(int u, int v, int weight) { if (u < 1 || u > vertexes_num || v < 1 || v > vertexes_num) { throw std::out_of_range("Vertex out of bounds in addEdge."); } @@ -39,17 +39,17 @@ void Graph::addEdge(int u, int v, int weight) { edges_num++; } -int Graph::getVertexesNum() { +int Graph::GetVertexesNum() { if (vertexes_num <= 0) throw std::runtime_error("Graph is not properly initialized."); return vertexes_num; } -int Graph::getEdgesNum() { return edges_num; } +int Graph::GetEdgesNum() { return edges_num; } -const AdjacencyList Graph::getAdjList() { return adjList; } +const AdjacencyList Graph::GetAdjList() { return adjList; } -void Graph::printGraph() const { +void Graph::PrintGraph() const { if (vertexes_num == 0) { std::cerr << "Error: Graph is empty." << std::endl; return; @@ -67,27 +67,27 @@ void Graph::printGraph() const { } } -std::vector> Graph::getNeighbours(int v) { +std::vector> Graph::GetNeighbours(int v) { if (v < 0 || v >= vertexes_num) { throw std::out_of_range("Vertex index out of bounds in getNeighbours."); } return adjList[v]; } -void Graph::top_sort(int v, int from, std::vector& visited, - std::vector& way) { +void Graph::TopSort(int v, int from, std::vector& visited, + std::vector& way) { if (visited[v]) return; visited[v] = true; way.push_back(v); - for (auto [u, w] : getNeighbours(v)) { + for (auto [u, w] : GetNeighbours(v)) { if (!visited[u]) { - top_sort(u, v, visited, way); + TopSort(u, v, visited, way); } } } -std::vector Graph::topological_sort(int start) { +std::vector Graph::TopologicalSort(int start) { if (vertexes_num == 0) { std::cerr << "Error: Graph is empty, topological sort cannot be performed." << std::endl; @@ -95,9 +95,9 @@ std::vector Graph::topological_sort(int start) { } std::vector way; - std::vector visited(getVertexesNum(), false); + std::vector visited(GetVertexesNum(), false); - top_sort(start, UNREACHABLE, visited, way); + TopSort(start, UNREACHABLE, visited, way); if (way.size() < vertexes_num) throw std::invalid_argument("Graph is disconnected."); @@ -105,7 +105,7 @@ std::vector Graph::topological_sort(int start) { return way; } -std::vector> Graph::getBridges() { +std::vector> Graph::GetBridges() { std::vector tin(vertexes_num, -1); std::vector low(vertexes_num, -1); std::vector visited(vertexes_num, false); @@ -114,14 +114,14 @@ std::vector> Graph::getBridges() { for (int i = 0; i < vertexes_num; i++) { if (!visited[i]) { - dfsBridges(i, -1, tin, low, visited, timer, bridges); + DfsBridges(i, -1, tin, low, visited, timer, bridges); } } return bridges; } -void Graph::dfsBridges(int v, int parent, std::vector& tin, +void Graph::DfsBridges(int v, int parent, std::vector& tin, std::vector& low, std::vector& visited, int& timer, std::vector>& bridges) { visited[v] = true; @@ -131,7 +131,7 @@ void Graph::dfsBridges(int v, int parent, std::vector& tin, if (u == parent) continue; if (!visited[u]) { - dfsBridges(u, v, tin, low, visited, timer, bridges); + DfsBridges(u, v, tin, low, visited, timer, bridges); low[v] = std::min(low[v], low[u]); if (low[u] > tin[v]) { @@ -143,7 +143,7 @@ void Graph::dfsBridges(int v, int parent, std::vector& tin, } } -std::vector Graph::getArticulationPoints() { +std::vector Graph::GetArticulationPoints() { std::vector tin(vertexes_num, -1); std::vector low(vertexes_num, -1); std::vector visited(vertexes_num, false); @@ -152,7 +152,7 @@ std::vector Graph::getArticulationPoints() { for (int i = 0; i < vertexes_num; i++) { if (!visited[i]) { - dfsArticulation(i, -1, tin, low, visited, timer, isArticulationPoint); + DfsArticulation(i, -1, tin, low, visited, timer, isArticulationPoint); } } @@ -166,7 +166,7 @@ std::vector Graph::getArticulationPoints() { return articulationPoints; } -void Graph::dfsArticulation(int v, int parent, std::vector& tin, +void Graph::DfsArticulation(int v, int parent, std::vector& tin, std::vector& low, std::vector& visited, int& timer, std::vector& isArticulationPoint) { @@ -179,7 +179,7 @@ void Graph::dfsArticulation(int v, int parent, std::vector& tin, if (!visited[u]) { children++; - dfsArticulation(u, v, tin, low, visited, timer, isArticulationPoint); + DfsArticulation(u, v, tin, low, visited, timer, isArticulationPoint); low[v] = std::min(low[v], low[u]); // Условие для точки сочленения diff --git a/lib/src/graph.hpp b/lib/src/graph.hpp index 0640ced..4d7469d 100644 --- a/lib/src/graph.hpp +++ b/lib/src/graph.hpp @@ -14,22 +14,22 @@ class Graph { Graph(int v, int e, bool directed); - void addEdge(int, int, int); + void AddEdge(int, int, int); - int getVertexesNum(); - int getEdgesNum(); - const AdjacencyList getAdjList(); + int GetVertexesNum(); + int GetEdgesNum(); + const AdjacencyList GetAdjList(); - std::vector> getNeighbours(int); + std::vector> GetNeighbours(int); - bool hasEdge(int, int) const; + bool HasEdge(int, int) const; - void printGraph() const; + void PrintGraph() const; - std::vector topological_sort(int); + std::vector TopologicalSort(int); - std::vector> getBridges(); - std::vector getArticulationPoints(); + std::vector> GetBridges(); + std::vector GetArticulationPoints(); private: int vertexes_num = 0; @@ -38,12 +38,12 @@ class Graph { AdjacencyList adjList; - void top_sort(int, int, std::vector&, std::vector&); + void TopSort(int, int, std::vector&, std::vector&); - void dfsBridges(int, int, std::vector&, std::vector&, + void DfsBridges(int, int, std::vector&, std::vector&, std::vector&, int&, std::vector>&); - void dfsArticulation(int, int, std::vector&, std::vector&, + void DfsArticulation(int, int, std::vector&, std::vector&, std::vector&, int&, std::vector&); }; }; // namespace algo From 367b32e82bb613c19e73cd82b54d294671cee635 Mon Sep 17 00:00:00 2001 From: Arkadiy <69438372+EchoPr@users.noreply.github.com> Date: Thu, 5 Dec 2024 12:10:14 +0000 Subject: [PATCH 12/16] refactor(style) --- task_01/src/main.cpp | 4 ++-- task_01/src/test.cpp | 32 ++++++++++++++++---------------- task_02/src/main.cpp | 14 +++++++------- task_02/src/test.cpp | 40 ++++++++++++++++++++-------------------- 4 files changed, 45 insertions(+), 45 deletions(-) diff --git a/task_01/src/main.cpp b/task_01/src/main.cpp index 98c6469..e2a9af2 100644 --- a/task_01/src/main.cpp +++ b/task_01/src/main.cpp @@ -13,11 +13,11 @@ int main() { std::cin >> a >> b >> w; - graph.addEdge(a, b, w); + graph.AddEdge(a, b, w); } std::cout << "Topsort:" << std::endl; - for (auto i : graph.topological_sort(0)) { + for (auto i : graph.TopologicalSort(0)) { std::cout << i + 1 << " "; } diff --git a/task_01/src/test.cpp b/task_01/src/test.cpp index 6b92957..9e6109b 100644 --- a/task_01/src/test.cpp +++ b/task_01/src/test.cpp @@ -7,15 +7,15 @@ using namespace algo; TEST(GraphTest, TopSort) { Graph graph(6, 5); - graph.addEdge(1, 2, 1); - graph.addEdge(1, 3, 2); - graph.addEdge(2, 4, 3); - graph.addEdge(3, 5, 4); - graph.addEdge(4, 6, 5); + graph.AddEdge(1, 2, 1); + graph.AddEdge(1, 3, 2); + graph.AddEdge(2, 4, 3); + graph.AddEdge(3, 5, 4); + graph.AddEdge(4, 6, 5); std::vector expected = {0, 1, 3, 5, 2, 4}; // Индексы вершин - auto calc = graph.topological_sort(0); + auto calc = graph.TopologicalSort(0); ASSERT_EQ(expected, calc); } @@ -25,7 +25,7 @@ TEST(GraphTest, TopSortEmpty) { { try { Graph graph(0, 0); - graph.topological_sort(0); + graph.TopologicalSort(0); } catch (const std::invalid_argument& e) { ASSERT_STREQ(e.what(), "Number of vertices must be greater than zero."); @@ -38,13 +38,13 @@ TEST(GraphTest, TopSortEmpty) { TEST(GraphTest, TopologicalSort_Disconnected) { Graph graph(5, 2); - graph.addEdge(1, 2, 1); - graph.addEdge(3, 4, 2); + graph.AddEdge(1, 2, 1); + graph.AddEdge(3, 4, 2); EXPECT_THROW( { try { - graph.topological_sort(0); + graph.TopologicalSort(0); } catch (const std::invalid_argument& e) { ASSERT_STREQ(e.what(), "Graph is disconnected."); throw; @@ -56,15 +56,15 @@ TEST(GraphTest, TopologicalSort_Disconnected) { TEST(GraphTest, TopologicalSort_LinearChain) { Graph graph(5, 4); - graph.addEdge(1, 2, 1); - graph.addEdge(2, 3, 2); - graph.addEdge(3, 4, 3); - graph.addEdge(4, 5, 4); + graph.AddEdge(1, 2, 1); + graph.AddEdge(2, 3, 2); + graph.AddEdge(3, 4, 3); + graph.AddEdge(4, 5, 4); std::vector expected_order = {0, 1, 2, 3, 4}; // Ожидаемый линейный порядок - auto actual_order = graph.topological_sort(0); + auto actual_order = graph.TopologicalSort(0); ASSERT_EQ(expected_order, actual_order); } @@ -75,7 +75,7 @@ TEST(GraphTest, TopologicalSort_EmptyEdgeList) { EXPECT_THROW( { try { - graph.topological_sort(0); + graph.TopologicalSort(0); } catch (const std::invalid_argument& e) { ASSERT_STREQ(e.what(), "Graph is disconnected."); throw; diff --git a/task_02/src/main.cpp b/task_02/src/main.cpp index 423951d..56a031c 100644 --- a/task_02/src/main.cpp +++ b/task_02/src/main.cpp @@ -6,14 +6,14 @@ using namespace algo; int main() { Graph graph(5, 5, false); - graph.addEdge(1, 2, 0); - graph.addEdge(1, 3, 0); - graph.addEdge(3, 4, 0); - graph.addEdge(4, 5, 0); - graph.addEdge(4, 2, 0); + graph.AddEdge(1, 2, 0); + graph.AddEdge(1, 3, 0); + graph.AddEdge(3, 4, 0); + graph.AddEdge(4, 5, 0); + graph.AddEdge(4, 2, 0); - auto bridges = graph.getBridges(); - auto articulationPoints = graph.getArticulationPoints(); + auto bridges = graph.GetBridges(); + auto articulationPoints = graph.GetArticulationPoints(); std::cout << "Bridges:\n"; for (auto [u, v] : bridges) { diff --git a/task_02/src/test.cpp b/task_02/src/test.cpp index 0d0429e..5372d8d 100644 --- a/task_02/src/test.cpp +++ b/task_02/src/test.cpp @@ -47,12 +47,12 @@ TEST(MinStackTest, Simple) { // Тест для поиска мостов TEST(GraphTest, getBridges) { Graph graph(5, 0, false); - graph.addEdge(1, 2, 0); - graph.addEdge(1, 3, 0); - graph.addEdge(3, 4, 0); - graph.addEdge(4, 5, 0); + graph.AddEdge(1, 2, 0); + graph.AddEdge(1, 3, 0); + graph.AddEdge(3, 4, 0); + graph.AddEdge(4, 5, 0); - auto bridges = graph.getBridges(); + auto bridges = graph.GetBridges(); // Ожидаем следующие мосты std::vector> expectedBridges = { @@ -67,12 +67,12 @@ TEST(GraphTest, getBridges) { // Тест для поиска точек сочленения TEST(GraphTest, getArticulationPoints) { Graph graph(5, 0, false); - graph.addEdge(1, 2, 0); - graph.addEdge(1, 3, 0); - graph.addEdge(3, 4, 0); - graph.addEdge(4, 5, 0); + graph.AddEdge(1, 2, 0); + graph.AddEdge(1, 3, 0); + graph.AddEdge(3, 4, 0); + graph.AddEdge(4, 5, 0); - auto articulationPoints = graph.getArticulationPoints(); + auto articulationPoints = graph.GetArticulationPoints(); // Ожидаем следующие точки сочленения std::vector expectedPoints = {1, 3, 4}; @@ -88,12 +88,12 @@ TEST(GraphTest, getArticulationPoints) { // Тест для графа без мостов TEST(GraphTest, NoBridges) { Graph graph(4, 0, false); - graph.addEdge(1, 2, 0); - graph.addEdge(2, 3, 0); - graph.addEdge(3, 4, 0); - graph.addEdge(4, 1, 0); + graph.AddEdge(1, 2, 0); + graph.AddEdge(2, 3, 0); + graph.AddEdge(3, 4, 0); + graph.AddEdge(4, 1, 0); - auto bridges = graph.getBridges(); + auto bridges = graph.GetBridges(); EXPECT_TRUE(bridges.empty()); } @@ -101,12 +101,12 @@ TEST(GraphTest, NoBridges) { // Тест для графа без точек сочленения TEST(GraphTest, NoArticulationPoints) { Graph graph(4, 0, false); - graph.addEdge(1, 2, 0); - graph.addEdge(2, 3, 0); - graph.addEdge(3, 4, 0); - graph.addEdge(4, 1, 0); + graph.AddEdge(1, 2, 0); + graph.AddEdge(2, 3, 0); + graph.AddEdge(3, 4, 0); + graph.AddEdge(4, 1, 0); - auto articulationPoints = graph.getArticulationPoints(); + auto articulationPoints = graph.GetArticulationPoints(); EXPECT_TRUE(articulationPoints.empty()); } From dec3df32afb2f8c9f0365f23f7b96d3b58b280e1 Mon Sep 17 00:00:00 2001 From: Arkadiy <69438372+EchoPr@users.noreply.github.com> Date: Thu, 5 Dec 2024 12:10:35 +0000 Subject: [PATCH 13/16] feat(tests): Tests added for Dijkstra --- task_04/src/main.cpp | 39 +++----------------- task_04/src/test.cpp | 85 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 34 deletions(-) diff --git a/task_04/src/main.cpp b/task_04/src/main.cpp index fedd50c..442b4dd 100644 --- a/task_04/src/main.cpp +++ b/task_04/src/main.cpp @@ -1,37 +1,12 @@ #include #include -#include -#include -#include #include "graph.hpp" +#include "util.hpp" using namespace algo; using namespace std; -void dijkstra(int v, int from, vector& vis, vector& dst, - const AdjacencyList adj) { - priority_queue, vector>, - greater>> - q; - - q.push({0, v}); - while (!q.empty()) { - auto [cur_d, v] = q.top(); - q.pop(); - - if (cur_d > dst[v]) continue; - - for (auto [u, w] : adj[v]) { - if (dst[u] > dst[v] + w) { - dst[u] = dst[v] + w; - - q.push({dst[u], u}); - } - } - } -} - int main() { int vertexes_num, edges_num; cin >> vertexes_num >> edges_num; @@ -43,20 +18,16 @@ int main() { cin >> a >> b >> w; - graph.addEdge(a, b, w); + graph.AddEdge(a, b, w); } - vector vis(vertexes_num); - vector dst(vertexes_num, INT_MAX); - int start; cin >> start; start--; - dst[start] = 0; - dijkstra(start, -1, vis, dst, graph.getAdjList()); + auto ways = FindWays(graph, start); - for (int i = 0; i < dst.size(); i++) { - cout << i + 1 << ": " << dst[i] << endl; + for (int i = 0; i < ways.size(); i++) { + cout << i + 1 << ": " << ways[i] << endl; } } diff --git a/task_04/src/test.cpp b/task_04/src/test.cpp index 5e11617..b85347b 100644 --- a/task_04/src/test.cpp +++ b/task_04/src/test.cpp @@ -1,6 +1,91 @@ #include +#include "graph.hpp" +#include "util.hpp" + +using namespace algo; + TEST(TopologySort, Simple) { ASSERT_EQ(1, 1); // Stack [] } + +void CheckDistances(const std::vector& actual, + const std::vector& expected) { + EXPECT_EQ(actual.size(), expected.size()); + for (size_t i = 0; i < expected.size(); ++i) { + EXPECT_EQ(actual[i], expected[i]); + } +} + +// Тест: стандартный граф с положительными весами +TEST(DijkstraTest, StandardGraph) { + Graph graph(6, 0, false); + graph.AddEdge(1, 2, 7); + graph.AddEdge(1, 3, 9); + graph.AddEdge(1, 6, 14); + graph.AddEdge(2, 3, 10); + graph.AddEdge(2, 4, 15); + graph.AddEdge(3, 4, 11); + graph.AddEdge(3, 6, 2); + graph.AddEdge(4, 5, 6); + graph.AddEdge(5, 6, 9); + + auto distances = FindWays(graph, 1); + + // Ожидаемые минимальные расстояния от вершины 1 + std::vector expected = {0, 7, 9, 20, 20, 11}; + CheckDistances(distances, expected); +} + +// Тест: граф с недостижимыми вершинами +TEST(DijkstraTest, GraphWithUnreachableNodes) { + Graph graph(4, 0, false); + graph.AddEdge(1, 2, 5); + graph.AddEdge(2, 3, 10); + // Вершина 4 изолирована + + auto distances = FindWays(graph, 1); + + // Ожидаемые минимальные расстояния от вершины 1 + std::vector expected = {0, 5, 15, std::numeric_limits::max()}; + CheckDistances(distances, expected); +} + +// Тест: граф с петлями +TEST(DijkstraTest, GraphWithSelfLoops) { + Graph graph(3, 0, false); + graph.AddEdge(1, 1, 10); // Петля на вершине 1 + graph.AddEdge(1, 2, 5); + graph.AddEdge(2, 3, 10); + + auto distances = FindWays(graph, 1); + + // Ожидаемые минимальные расстояния от вершины 1 + std::vector expected = {0, 5, 15}; + CheckDistances(distances, expected); +} + +// Тест: граф с одной вершиной +TEST(DijkstraTest, SingleNodeGraph) { + Graph graph(1, 0, false); + + auto distances = FindWays(graph, 1); + + // Ожидаемые минимальные расстояния от вершины 1 + std::vector expected = {0}; + CheckDistances(distances, expected); +} + +// Тест: граф без рёбер +TEST(DijkstraTest, GraphWithoutEdges) { + Graph graph(4, 0, false); + + auto distances = FindWays(graph, 1); + + // Ожидаемые минимальные расстояния от вершины 1 + std::vector expected = {0, std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::max()}; + CheckDistances(distances, expected); +} \ No newline at end of file From 7e77eed60443d0802763f5eac4070b45d3d88af2 Mon Sep 17 00:00:00 2001 From: Arkadiy <69438372+EchoPr@users.noreply.github.com> Date: Thu, 5 Dec 2024 14:05:33 +0000 Subject: [PATCH 14/16] refactor(whole) --- lib/src/graph.cpp | 11 +++++------ lib/src/graph.hpp | 2 +- lib/src/util.cpp | 6 +++--- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/lib/src/graph.cpp b/lib/src/graph.cpp index 7f9c706..0d4345a 100644 --- a/lib/src/graph.cpp +++ b/lib/src/graph.cpp @@ -74,30 +74,29 @@ std::vector> Graph::GetNeighbours(int v) { return adjList[v]; } -void Graph::TopSort(int v, int from, std::vector& visited, - std::vector& way) { +void Graph::TopSort(int v, std::vector& visited, std::vector& way) { if (visited[v]) return; visited[v] = true; way.push_back(v); for (auto [u, w] : GetNeighbours(v)) { if (!visited[u]) { - TopSort(u, v, visited, way); + TopSort(u, visited, way); } } } std::vector Graph::TopologicalSort(int start) { if (vertexes_num == 0) { - std::cerr << "Error: Graph is empty, topological sort cannot be performed." - << std::endl; + std::cerr + << "Error: Graph is empty, topological sort cannot be performed.\n"; return {}; } std::vector way; std::vector visited(GetVertexesNum(), false); - TopSort(start, UNREACHABLE, visited, way); + TopSort(start, visited, way); if (way.size() < vertexes_num) throw std::invalid_argument("Graph is disconnected."); diff --git a/lib/src/graph.hpp b/lib/src/graph.hpp index 4d7469d..16891b7 100644 --- a/lib/src/graph.hpp +++ b/lib/src/graph.hpp @@ -38,7 +38,7 @@ class Graph { AdjacencyList adjList; - void TopSort(int, int, std::vector&, std::vector&); + void TopSort(int, std::vector&, std::vector&); void DfsBridges(int, int, std::vector&, std::vector&, std::vector&, int&, std::vector>&); diff --git a/lib/src/util.cpp b/lib/src/util.cpp index e2f9bb4..ceac1d5 100644 --- a/lib/src/util.cpp +++ b/lib/src/util.cpp @@ -22,9 +22,9 @@ vector FindWays(algo::Graph graph, int start) { void Dijkstra(int v, int from, vector& vis, vector& dst, const AdjacencyList adj) { - priority_queue, vector>, - greater>> - q; + using Pair = pair; + + priority_queue, greater> q; q.push({0, v}); while (!q.empty()) { From fd9bea496ba3a0306636c8b4b514c86015ec1ed0 Mon Sep 17 00:00:00 2001 From: Arkadiy <69438372+EchoPr@users.noreply.github.com> Date: Sat, 14 Dec 2024 17:04:51 +0300 Subject: [PATCH 15/16] Update lib/src/graph.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- lib/src/graph.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/graph.cpp b/lib/src/graph.cpp index 0d4345a..650996a 100644 --- a/lib/src/graph.cpp +++ b/lib/src/graph.cpp @@ -3,7 +3,7 @@ #include #include -#define UNREACHABLE -1 +#define UNREACHABLE (-1) using namespace algo; From 4aea517f4d9cf6974303b900a06d0b4d5ea095de Mon Sep 17 00:00:00 2001 From: Arkadiy <69438372+EchoPr@users.noreply.github.com> Date: Sat, 14 Dec 2024 17:05:46 +0300 Subject: [PATCH 16/16] Update lib/src/graph.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- lib/src/graph.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/graph.cpp b/lib/src/graph.cpp index 650996a..b58dd7f 100644 --- a/lib/src/graph.cpp +++ b/lib/src/graph.cpp @@ -63,7 +63,7 @@ void Graph::PrintGraph() const { for (const auto& [neighbor, weight] : adjList[i]) { std::cout << "(" << neighbor + 1 << ", " << weight << ") "; } - std::cout << std::endl; + std::cout << '\n'; } }