diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 98320e1..983320b 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -7,4 +7,5 @@ file(GLOB_RECURSE lib_source_list "src/*.cpp" "src/*.hpp") add_library(${PROJECT_NAME} ${lib_source_list}) +set_target_properties(${PROJECT_NAME} PROPERTIES LINKER_LANGUAGE CXX) target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src) diff --git a/lib/src/graph/graph.hpp b/lib/src/graph/graph.hpp new file mode 100644 index 0000000..611ecb4 --- /dev/null +++ b/lib/src/graph/graph.hpp @@ -0,0 +1,193 @@ +#pragma once + +#include + +#include "vertex.hpp" + +template +class Graph { + public: + Graph(bool is_oriented = true) : is_oriented(is_oriented) {} + + Graph(const std::vector>& vertices, bool is_oriented = true) + : vertices(vertices), is_oriented(is_oriented) {} + + Graph(const std::vector>& edges, bool is_oriented = true) + : is_oriented(is_oriented) { + std::map> vertices_to_add; + + for (auto edge : edges) { + vertices_to_add[edge.first].push_back(edge.second); + if (!is_oriented) vertices_to_add[edge.second].push_back(edge.first); + } + + for (auto vertex : vertices_to_add) { + vertices.push_back(Vertex(vertex.first, vertex.second)); + } + } + + void PrintAdjList() const { + for (auto vertex : vertices) { + vertex.PrintAdjVertices(); + } + } + + bool IsOriented() const { return is_oriented; } + + std::vector> GetVertices() const { return vertices; } + + std::vector GetVerticesIds() const { + std::vector vertices_ids; + for (int i = 0; i < vertices.size(); i++) + vertices_ids.push_back(vertices[i].GetVertexId()); + return vertices_ids; + } + + std::vector GetAdjVertices(const T& vertex) const { + if (!ContainsVertex(vertex)) + throw std::invalid_argument("Vertex not found!"); + + std::vector adj_vertices; + for (int i = 0; i < vertices.size(); i++) { + if (vertices[i].GetVertexId() == vertex) { + adj_vertices = vertices[i].GetAdjVertices(); + break; + } + } + return adj_vertices; + } + + size_t GetVerticesCount() const { return vertices.size(); } + + size_t GetEdgesCount() const { + size_t count = 0; + for (int i = 0; i < vertices.size(); i++) + count += vertices[i].GetAdjVerticesCount(); + // У неориентированного графа в два раза меньше ребер, чем у такого же + // ориентированного + if (!is_oriented) count /= 2; + + return count; + } + + bool ContainsVertex(const Vertex& vertex) const { + for (auto vert : vertices) { + if (vert.GetVertexId() == vertex.GetVertexId() && + vert.GetAdjVerticesCount() == vertex.GetAdjVerticesCount()) { + // Проверяем смежности + auto adj_verts = vertex.GetAdjVertices(); + for (int i = 0; i < adj_verts.size(); i++) { + if (!vertex.ContainsAdjVertex(adj_verts[i])) return false; + } + return true; + } + } + return false; + } + + bool ContainsVertex(const T& vertex) const { + for (auto vert : vertices) { + if (vert.GetVertexId() == vertex) return true; + } + return false; + } + + bool ContainsEdge(const T& vert_1, const T& vert_2) const { + for (int i = 0; i < vertices.size(); i++) { + if (vert_1 == vertices[i].GetVertexId() && + vertices[i].ContainsAdjVertex(vert_2)) + return true; + } + return false; + } + + void AddVertex(const Vertex& vertex) { + if (is_oriented) { + vertices.push_back(vertex); + return; + } + + // Добавление вершины для неориентированного графа + for (auto adj_vertex : vertex.GetAdjVertices()) { + if (!ContainsVertex(adj_vertex)) AddVertex(adj_vertex); + + for (int i = 0; i < vertices.size(); i++) { + if (adj_vertex == vertices[i].GetVertexId()) { + vertices[i].AddAdjVertex(vertex.GetVertexId()); + break; + } + } + } + vertices.push_back(vertex); + } + + void AddVertex(const T& vertex) { vertices.push_back(Vertex(vertex)); } + + void AddEdge(const T& vert_1, const T& vert_2) { + if (!ContainsVertex(vert_1)) AddVertex(vert_1); + if (!ContainsVertex(vert_2)) AddVertex(vert_2); + + for (int i = 0; i < vertices.size(); i++) { + if (vertices[i].GetVertexId() == vert_1) { + vertices[i].AddAdjVertex(vert_2); + if (is_oriented) return; + break; + } + } + + // Если граф неориентированный, то мы добавляем ребро vert_2->vert_1 + for (int i = 0; i < vertices.size(); i++) { + if (vertices[i].GetVertexId() == vert_2) { + vertices[i].AddAdjVertex(vert_1); + return; + } + } + } + + void DeleteVertex(const T& vertex) { + if (!ContainsVertex(vertex)) + throw std::invalid_argument("Vertex not found!"); + + for (int i = 0; i < vertices.size(); i++) { + if (vertex == vertices[i].GetVertexId()) { + vertices.erase(vertices.begin() + i); + + // Удаляем смежности с удаленной вершиной + for (int i = 0; i < vertices.size(); i++) { + if (vertices[i].ContainsAdjVertex(vertex)) + vertices[i].DeleteAdjVertex(vertex); + } + return; + } + } + } + + void DeleteEdge(const T& vert_1, const T& vert_2) { + if (!ContainsVertex(vert_1)) throw std::invalid_argument("Edge not found!"); + + for (int i = 0; i < vertices.size(); i++) { + if (vertices[i].GetVertexId() == vert_1) { + vertices[i].DeleteAdjVertex(vert_2); + if (is_oriented) + return; + else + break; + } + } + + // Для неориентированного графа нужно удалить и у vert_2 смежную вершину + // vert_1 + if (!ContainsVertex(vert_2)) throw std::invalid_argument("Edge not found!"); + + for (int i = 0; i < vertices.size(); i++) { + if (vertices[i].GetVertexId() == vert_2) { + vertices[i].DeleteAdjVertex(vert_1); + return; + } + } + } + + private: + std::vector> vertices; + bool is_oriented; +}; \ No newline at end of file diff --git a/lib/src/graph/vertex.hpp b/lib/src/graph/vertex.hpp new file mode 100644 index 0000000..5eae3ba --- /dev/null +++ b/lib/src/graph/vertex.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include +#include +#include + +template +class Vertex { + public: + Vertex(const T& vertex_id, const std::vector& adj_vertices = {}) + : vertex_id(vertex_id), adjacent_vertices(adj_vertices) {} + + void AddAdjVertex(const T& adj_vertex) { + if (ContainsAdjVertex(adj_vertex)) return; + adjacent_vertices.push_back(adj_vertex); + } + + void DeleteAdjVertex(const T& adj_vertex) { + for (int i = 0; i < adjacent_vertices.size(); i++) { + if (adjacent_vertices[i] == adj_vertex) { + adjacent_vertices.erase(adjacent_vertices.begin() + i); + return; + } + } + throw std::invalid_argument("Adjacent vertex not found!"); + } + + T GetVertexId() const { return vertex_id; } + + std::vector GetAdjVertices() const { return adjacent_vertices; } + + size_t GetAdjVerticesCount() const { return adjacent_vertices.size(); } + + void PrintAdjVertices() const { + std::cout << "Adjacent vertices for " << vertex_id << ": {"; + for (int i = 0; i < adjacent_vertices.size(); i++) { + std::cout << adjacent_vertices[i]; + if (i != adjacent_vertices.size() - 1) std::cout << ", "; + } + std::cout << "}\n"; + } + + bool ContainsAdjVertex(const T& adj_vert) const { + for (auto vertex : adjacent_vertices) { + if (adj_vert == vertex) return true; + } + return false; + } + + private: + T vertex_id; + std::vector adjacent_vertices; +}; \ No newline at end of file diff --git a/lib/src/graph/weighted_graph.hpp b/lib/src/graph/weighted_graph.hpp new file mode 100644 index 0000000..17500f5 --- /dev/null +++ b/lib/src/graph/weighted_graph.hpp @@ -0,0 +1,163 @@ +#pragma once +#include + +#include "graph.hpp" + +template +struct WeightedEdge { + public: + WeightedEdge(const T& start_vertex, const T& end_vertex, int weight) + : start_vertex(start_vertex), end_vertex(end_vertex), weight(weight) {} + + T start_vertex; + T end_vertex; + int weight; +}; + +template +class WeightedGraph { + public: + WeightedGraph(bool is_oriented = true) : graph(is_oriented) {} + + WeightedGraph(const std::vector, int>>& w_edges, + bool is_oriented = true) + : graph(is_oriented) { + for (auto w_edge : w_edges) { + auto edge = w_edge.first; + graph.AddEdge(edge.first, edge.second); + weighted_edges.push_back( + WeightedEdge(edge.first, edge.second, w_edge.second)); + if (!graph.IsOriented()) + weighted_edges.push_back( + WeightedEdge(edge.second, edge.first, w_edge.second)); + } + } + + WeightedGraph(const std::vector>& edges, + const std::vector weights, bool is_oriented = true) + : graph(is_oriented) { + if (edges.size() != weights.size()) + throw std::invalid_argument( + "The number of weighted edges is greater than the number of all " + "edges in the graph!"); + for (int i = 0; i < edges.size(); i++) { + graph.AddEdge(edges[i].first, edges[i].second); + weighted_edges.push_back( + WeightedEdge(edges[i].first, edges[i].second, weights[i])); + if (!graph.IsOriented()) + weighted_edges.push_back( + WeightedEdge(edges[i].second, edges[i].first, weights[i])); + } + } + + void PrintAdjList() { + for (auto vertex : graph.GetVertices()) { + std::cout << "Adjacent vertices for '" << vertex.GetVertexId() << "': {"; + auto adj_verts = vertex.GetAdjVertices(); + for (int i = 0; i < adj_verts.size(); i++) { + std::cout << "('" << adj_verts[i] << "', " + << GetEdgeWeight(vertex.GetVertexId(), adj_verts[i]) << ")"; + if (i != adj_verts.size() - 1) std::cout << ", "; + } + std::cout << "}\n"; + } + } + + bool IsOriented() const { return graph.IsOriented(); } + + std::vector> GetWeightedEdges() const { + return weighted_edges; + } + + std::vector> GetVertices() const { return graph.GetVertices(); } + + std::vector GetVerticesIds() const { return graph.GetVerticesIds(); } + + std::vector GetAdjVertices(const T& vertex) const { + return graph.GetAdjVertices(vertex); + } + + size_t GetVerticesCount() const { return graph.GetVerticesCount(); } + + size_t GetEdgesCount() const { return graph.GetEdgesCount(); } + + int GetEdgeWeight(const T& start, const T& end) const { + for (auto w_edge : weighted_edges) { + if (w_edge.start_vertex == start && w_edge.end_vertex == end) + return w_edge.weight; + } + throw std::invalid_argument("Edge not found!"); + } + + bool ContainsVertex(const T& vertex) const { + return graph.ContainsVertex(vertex); + } + + bool ContainsEdge(const T& start, const T& end) const { + return graph.ContainsEdge(start, end); + } + + void AddVertex(const T& vertex) { graph.AddVertex(vertex); } + + void AddWeightedEdge(const T& start, const T& end, int weight = 0) { + graph.AddEdge(start, end); + weighted_edges.push_back(WeightedEdge(start, end, weight)); + if (!graph.IsOriented()) + weighted_edges.push_back(WeightedEdge(end, start, weight)); + } + + void DeleteVertex(const T& vertex) { + graph.DeleteVertex(vertex); + + int end = weighted_edges.size(); + for (int i = 0; i < end; i++) { + WeightedEdge w_edge = weighted_edges[i]; + if (!graph.ContainsEdge(w_edge.start_vertex, w_edge.end_vertex)) { + weighted_edges.erase(weighted_edges.begin() + i); + i--; + end--; + } + } + } + + void DeleteWeightedEdge(const T& start, const T& end) { + graph.DeleteEdge(start, end); + + for (int i = 0; i < weighted_edges.size(); i++) { + if (weighted_edges[i].start_vertex == start && + weighted_edges[i].end_vertex == end) { + weighted_edges.erase(weighted_edges.begin() + i); + if (graph.IsOriented()) return; + } + } + for (int i = 0; i < weighted_edges.size(); i++) { + if (weighted_edges[i].start_vertex == end && + weighted_edges[i].end_vertex == start) + weighted_edges.erase(weighted_edges.begin() + i); + } + } + + void SetEdgeWeight(const T& start, const T& end, int w) { + if (!graph.ContainsEdge(start, end)) + throw std::invalid_argument("Edge not found!"); + + for (int i = 0; i < weighted_edges.size(); i++) { + if (weighted_edges[i].start_vertex == start && + weighted_edges[i].end_vertex == end) { + weighted_edges[i].weight = w; + if (graph.IsOriented()) return; + } + } + for (int i = 0; i < weighted_edges.size(); i++) { + if (weighted_edges[i].start_vertex == end && + weighted_edges[i].end_vertex == start) { + weighted_edges[i].weight = w; + return; + } + } + } + + private: + Graph graph; + std::vector> weighted_edges; +}; \ No newline at end of file diff --git a/lib/src/util.cpp b/lib/src/util.cpp deleted file mode 100644 index 81e15bd..0000000 --- a/lib/src/util.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "util.hpp" diff --git a/lib/src/util.hpp b/lib/src/util.hpp deleted file mode 100644 index e69de29..0000000 diff --git a/sandbox/for_graph/CMakeLists.txt b/sandbox/for_graph/CMakeLists.txt new file mode 100644 index 0000000..ae49c5d --- /dev/null +++ b/sandbox/for_graph/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 3.10) + +get_filename_component(PROJECT_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) +string(REPLACE " " "_" PROJECT_NAME ${PROJECT_NAME}) +project(${PROJECT_NAME} C CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +file(GLOB_RECURSE source_list "src/*.cpp" "src/*.hpp") +file(GLOB_RECURSE lib_source_list "../../lib/src/*.cpp" "../../lib/src/*.hpp") +file(GLOB_RECURSE main_source_list "src/main.cpp") +file(GLOB_RECURSE test_source_list "src/*.cpp") +file(GLOB_RECURSE test_list "src/*test.cpp") + +list(REMOVE_ITEM test_source_list ${main_source_list}) +list(REMOVE_ITEM source_list ${test_list}) + +include_directories(${PROJECT_NAME} PUBLIC src) +include_directories(${PROJECT_NAME} PUBLIC ../lib/src) + +add_executable(${PROJECT_NAME} ${source_list}) +target_link_libraries(${PROJECT_NAME} PUBLIC Utils) + +# Locate GTest +enable_testing() +find_package(GTest REQUIRED) +include_directories(${GTEST_INCLUDE_DIRS}) + +# Link runTests with what we want to test and the GTest and pthread library +add_executable(${PROJECT_NAME}_tests ${test_source_list}) +target_link_libraries( + ${PROJECT_NAME}_tests + GTest::gtest_main + Utils +) + +include(GoogleTest) +gtest_discover_tests(${PROJECT_NAME}_tests) diff --git a/sandbox/for_graph/src/graph_test.cpp b/sandbox/for_graph/src/graph_test.cpp new file mode 100644 index 0000000..e3f0f74 --- /dev/null +++ b/sandbox/for_graph/src/graph_test.cpp @@ -0,0 +1,286 @@ +#include "graph/graph.hpp" + +#include + +TEST(Test_NotOrientedGraph, Test_Constructors) { + Vertex v1(1, {2, 4, 3}); + Vertex v2(2, {1, 3, 4}); + Vertex v3(3, {1, 2, 4, 5}); + Vertex v4(4, {3, 5, 2, 1}); + Vertex v5(5, {4, 3}); + + Graph g1({v1, v2, v3, v4, v5}); + + std::vector> edges{{1, 2}, {1, 4}, {1, 3}, {2, 3}, + {2, 4}, {3, 4}, {3, 5}, {5, 4}}; + + Graph g2(edges, false); + + for (int i = 0; i < g1.GetVerticesCount(); i++) { + ASSERT_TRUE(g2.ContainsVertex(g1.GetVertices()[i])); + } +} + +TEST(Test_NotOrientedGraph, Test_GetEdgesCount) { + std::vector> edges = { + {'A', 'B'}, {'A', 'D'}, {'B', 'C'}, {'D', 'B'}, {'E', 'C'}, {'D', 'C'}}; + + Graph g(edges, false); + + ASSERT_EQ(edges.size(), g.GetEdgesCount()); +} + +TEST(Test_NotOrientedGraph, Test_ContainsEdge) { + Graph g( + {{'A', 'B'}, {'A', 'C'}, {'A', 'D'}, {'B', 'D'}, {'B', 'C'}, {'B', 'D'}}, + false); + + ASSERT_TRUE(g.ContainsEdge('A', 'C')); + ASSERT_TRUE(g.ContainsEdge('C', 'A')); + ASSERT_FALSE(g.ContainsEdge('C', 'D')); + ASSERT_FALSE(g.ContainsEdge('D', 'C')); +} + +TEST(Test_NotOrientedGraph, Test_ContainsVertex) { + Graph g({{'A', 'B'}, + {'A', 'C'}, + {'A', 'D'}, + {'B', 'C'}, + {'B', 'E'}, + {'C', 'F'}, + {'D', 'E'}, + {'E', 'F'}, + {'F', 'G'}}, + false); + + ASSERT_TRUE(g.ContainsVertex('A')); + ASSERT_FALSE(g.ContainsVertex('T')); + + ASSERT_TRUE(g.ContainsVertex(Vertex('G', {'F'}))); + ASSERT_TRUE(g.ContainsVertex(Vertex('A', {'C', 'D', 'B'}))); + ASSERT_FALSE(g.ContainsVertex(Vertex('Y', {'C', 'H', 'F'}))); + ASSERT_FALSE(g.ContainsVertex(Vertex('A', {'B', 'C'}))); +} + +TEST(Test_NotOrientedGraph, Test_DeleteVertex) { + Graph g({{'A', 'B'}, + {'A', 'C'}, + {'A', 'D'}, + {'B', 'C'}, + {'B', 'E'}, + {'C', 'F'}, + {'D', 'E'}, + {'E', 'F'}, + {'F', 'G'}, + {'A', 'F'}, + {'B', 'G'}}, + false); + + g.DeleteVertex('A'); + + ASSERT_FALSE(g.ContainsEdge('A', 'B')); + ASSERT_FALSE(g.ContainsEdge('B', 'A')); + ASSERT_FALSE(g.ContainsEdge('A', 'C')); + ASSERT_FALSE(g.ContainsVertex('A')); + ASSERT_TRUE(g.ContainsEdge('B', 'C')); + + ASSERT_THROW(g.DeleteVertex('T'), std::invalid_argument); +} + +TEST(Test_NotOrientedGraph, Test_DeleteEdge) { + Graph g({{'A', 'B'}, + {'A', 'C'}, + {'A', 'D'}, + {'B', 'C'}, + {'B', 'E'}, + {'C', 'F'}, + {'D', 'E'}, + {'E', 'F'}, + {'F', 'G'}, + {'A', 'F'}, + {'B', 'G'}}, + false); + + g.DeleteEdge('A', 'B'); + + ASSERT_FALSE(g.ContainsEdge('A', 'B')); + ASSERT_FALSE(g.ContainsEdge('B', 'A')); + ASSERT_TRUE(g.ContainsEdge('A', 'C')); + ASSERT_TRUE(g.ContainsEdge('C', 'A')); + + ASSERT_THROW(g.DeleteEdge('A', 'B'), std::invalid_argument); + ASSERT_THROW(g.DeleteEdge('E', 'G'), std::invalid_argument); +} + +TEST(Test_NotOrientedGraph, Test_AddVertex_AddEdge) { + Graph g({{'A', 'B'}, {'A', 'C'}, {'B', 'C'}, {'B', 'D'}, {'C', 'D'}}, + false); + + g.AddVertex('F'); + + ASSERT_TRUE(g.ContainsVertex('F')); + + g.AddEdge('F', 'B'); + g.AddEdge('A', 'F'); + + ASSERT_TRUE(g.ContainsEdge('F', 'B')); + ASSERT_TRUE(g.ContainsEdge('B', 'F')); + ASSERT_TRUE(g.ContainsEdge('F', 'A')); + ASSERT_TRUE(g.ContainsEdge('A', 'F')); + ASSERT_FALSE(g.ContainsEdge('F', 'C')); + ASSERT_FALSE(g.ContainsEdge('C', 'F')); + + g.AddVertex(Vertex('G', {'A', 'F'})); + + ASSERT_TRUE(g.ContainsEdge('G', 'A')); + ASSERT_TRUE(g.ContainsEdge('A', 'G')); + ASSERT_TRUE(g.ContainsEdge('F', 'G')); + ASSERT_TRUE(g.ContainsEdge('G', 'F')); + ASSERT_FALSE(g.ContainsEdge('G', 'B')); + ASSERT_FALSE(g.ContainsEdge('B', 'G')); +} + +TEST(Test_OrientedGraph, Test_Constructors) { + Vertex v1('A', {'B', 'C'}); + Vertex v2('B', {'D'}); + Vertex v3('C', {'E'}); + Vertex v4('D', {'A'}); + Vertex v5('E', {'B'}); + + Graph g1({v1, v2, v3, v4, v5}); + + std::vector> edges{{'A', 'B'}, {'A', 'C'}, {'B', 'D'}, + {'C', 'E'}, {'D', 'A'}, {'E', 'B'}}; + + Graph g2(edges); + + for (int i = 0; i < g1.GetVerticesCount(); i++) { + ASSERT_TRUE(g2.ContainsVertex(g1.GetVertices()[i])); + } +} + +TEST(Test_OrientedGraph, Test_GetEdgesCount) { + std::vector> edges = { + {'A', 'B'}, {'B', 'A'}, {'C', 'B'}, {'D', 'A'}, {'C', 'D'}, {'B', 'D'}}; + + Graph g(edges); + + ASSERT_EQ(edges.size(), g.GetEdgesCount()); +} + +TEST(Test_OrientedGraph, Test_ContainsEdge) { + Graph g({ + {'A', 'B'}, + {'A', 'C'}, + {'A', 'D'}, + {'B', 'C'}, + {'B', 'E'}, + {'C', 'A'}, + {'C', 'D'}, + {'C', 'E'}, + {'D', 'B'}, + {'E', 'A'}, + }); + + ASSERT_TRUE(g.ContainsEdge('A', 'C')); + ASSERT_TRUE(g.ContainsEdge('C', 'A')); + ASSERT_FALSE(g.ContainsEdge('C', 'F')); + ASSERT_FALSE(g.ContainsEdge('E', 'C')); + ASSERT_FALSE(g.ContainsEdge('D', 'C')); + ASSERT_FALSE(g.ContainsEdge('B', 'D')); +} + +TEST(Test_OrientedGraph, Test_ContainsVertex) { + Vertex v1('A', {'B', 'C', 'D', 'F'}); + Vertex v2('B', {'C', 'E', 'F'}); + Vertex v3('C', {'A', 'D', 'E', 'F'}); + Vertex v4('D', {'B', 'F'}); + Vertex v5('E', {'G'}); + Vertex v6('F', {'A', 'C'}); + Vertex v7('G', {'E'}); + + Graph g({v1, v2, v3, v4, v5, v6, v7}); + + ASSERT_TRUE(g.ContainsVertex('A')); + ASSERT_FALSE(g.ContainsVertex('T')); + + ASSERT_TRUE(g.ContainsVertex(Vertex('G', {'E'}))); + ASSERT_TRUE(g.ContainsVertex(Vertex('A', {'B', 'C', 'D', 'F'}))); + ASSERT_FALSE(g.ContainsVertex(Vertex('Y', {'C', 'H', 'F'}))); + ASSERT_FALSE(g.ContainsVertex(Vertex('A', {'B', 'C'}))); + ASSERT_FALSE(g.ContainsVertex(Vertex('G', {'E', 'A'}))); +} + +TEST(Test_OrientedGraph, Test_DeleteVertex) { + Vertex v1('A', {'B', 'C', 'D', 'F'}); + Vertex v2('B', {'C', 'E', 'F'}); + Vertex v3('C', {'A', 'D', 'E', 'F'}); + Vertex v4('D', {'B', 'F'}); + Vertex v5('E', {'G'}); + Vertex v6('F', {'A', 'C'}); + Vertex v7('G', {'E'}); + + Graph g({v1, v2, v3, v4, v5, v6, v7}); + + g.DeleteVertex('A'); + + ASSERT_FALSE(g.ContainsEdge('A', 'B')); + ASSERT_FALSE(g.ContainsEdge('B', 'A')); + ASSERT_FALSE(g.ContainsEdge('A', 'C')); + ASSERT_FALSE(g.ContainsEdge('C', 'A')); + ASSERT_FALSE(g.ContainsVertex('A')); + ASSERT_TRUE(g.ContainsEdge('B', 'C')); + ASSERT_TRUE(g.ContainsEdge('C', 'D')); + ASSERT_TRUE(g.ContainsEdge('G', 'E')); + + ASSERT_THROW(g.DeleteVertex('T'), std::invalid_argument); +} + +TEST(Test_OrientedGraph, Test_DeleteEdge) { + Vertex v1('A', {'B', 'C', 'D', 'F'}); + Vertex v2('B', {'C', 'E', 'F'}); + Vertex v3('C', {'A', 'D', 'E', 'F'}); + Vertex v4('D', {'B', 'F'}); + Vertex v5('E', {'G'}); + Vertex v6('F', {'A', 'C'}); + Vertex v7('G', {'E'}); + + Graph g({v1, v2, v3, v4, v5, v6, v7}); + + g.DeleteEdge('A', 'C'); + + ASSERT_FALSE(g.ContainsEdge('A', 'C')); + ASSERT_TRUE(g.ContainsEdge('C', 'A')); + ASSERT_TRUE(g.ContainsEdge('A', 'B')); + ASSERT_TRUE(g.ContainsEdge('A', 'D')); + + ASSERT_THROW(g.DeleteEdge('E', 'F'), std::invalid_argument); +} + +TEST(Test_OrientedGraph, Test_AddVertex_AddEdge) { + Graph g( + {{'A', 'B'}, {'A', 'C'}, {'B', 'A'}, {'B', 'D'}, {'C', 'D'}, {'D', 'A'}}); + + g.AddVertex('F'); + + ASSERT_TRUE(g.ContainsVertex('F')); + + g.AddEdge('F', 'B'); + g.AddEdge('A', 'F'); + + ASSERT_TRUE(g.ContainsEdge('F', 'B')); + ASSERT_FALSE(g.ContainsEdge('B', 'F')); + ASSERT_FALSE(g.ContainsEdge('F', 'A')); + ASSERT_TRUE(g.ContainsEdge('A', 'F')); + ASSERT_FALSE(g.ContainsEdge('F', 'C')); + ASSERT_FALSE(g.ContainsEdge('C', 'F')); + + g.AddVertex(Vertex('G', {'A', 'F'})); + + ASSERT_TRUE(g.ContainsEdge('G', 'A')); + ASSERT_FALSE(g.ContainsEdge('A', 'G')); + ASSERT_FALSE(g.ContainsEdge('F', 'G')); + ASSERT_TRUE(g.ContainsEdge('G', 'F')); + ASSERT_FALSE(g.ContainsEdge('G', 'B')); + ASSERT_FALSE(g.ContainsEdge('B', 'G')); +} \ No newline at end of file diff --git a/sandbox/for_graph/src/main.cpp b/sandbox/for_graph/src/main.cpp new file mode 100644 index 0000000..c3aeba4 --- /dev/null +++ b/sandbox/for_graph/src/main.cpp @@ -0,0 +1 @@ +int main() { return 0; } \ No newline at end of file diff --git a/sandbox/for_graph/src/vertex_test.cpp b/sandbox/for_graph/src/vertex_test.cpp new file mode 100644 index 0000000..ad2f066 --- /dev/null +++ b/sandbox/for_graph/src/vertex_test.cpp @@ -0,0 +1,77 @@ +#include "graph/vertex.hpp" + +#include + +#include + +TEST(TestVertex, Test_Constructors_1) { + Vertex v1(0, {1, 5, 3, 7, 4, 8}); + Vertex v2(0, std::vector{1, 5, 3, 7, 4, 8}); + + ASSERT_EQ(v1.GetVertexId(), v2.GetVertexId()); + + ASSERT_EQ(v1.GetAdjVertices().size(), v2.GetAdjVertices().size()); + + for (int i = 0; i < v1.GetAdjVertices().size(); i++) { + ASSERT_EQ(v1.GetAdjVertices()[i], v2.GetAdjVertices()[i]); + } +} + +TEST(TestVertex, Test_Constructors_2) { + Vertex v1('A', {'B', 'C', 'E', 'D', 'G'}); + Vertex v2('A', std::vector{'B', 'C', 'E', 'D', 'G'}); + + ASSERT_EQ(v1.GetVertexId(), v2.GetVertexId()); + + ASSERT_EQ(v1.GetAdjVertices().size(), v2.GetAdjVertices().size()); + + for (int i = 0; i < v1.GetAdjVertices().size(); i++) { + ASSERT_EQ(v1.GetAdjVertices()[i], v2.GetAdjVertices()[i]); + } +} + +TEST(TestVertex, Test_Constructors_3) { + Vertex v1("A", {"B", "C", "E", "D", "G"}); + Vertex v2("A", + std::vector{"B", "C", "E", "D", "G"}); + + ASSERT_EQ(v1.GetVertexId(), v2.GetVertexId()); + + ASSERT_EQ(v1.GetAdjVertices().size(), v2.GetAdjVertices().size()); + + for (int i = 0; i < v1.GetAdjVertices().size(); i++) { + ASSERT_EQ(v1.GetAdjVertices()[i], v2.GetAdjVertices()[i]); + } +} + +TEST(TestVertex, Test_AddAdjVertex) { + Vertex v1("A", {"B", "C", "E"}); + v1.AddAdjVertex("D"); + + std::vector expected_vertices{"B", "C", "E", "D"}; + + ASSERT_EQ(v1.GetAdjVertices(), expected_vertices); +} + +TEST(TestVertex, Test_DeleteAdjVertex) { + Vertex v1("A", {"B", "C", "E"}); + v1.DeleteAdjVertex("C"); + + std::vector expected_vertices{"B", "E"}; + + ASSERT_EQ(v1.GetAdjVertices(), expected_vertices); + + ASSERT_THROW(v1.DeleteAdjVertex("D"), std::invalid_argument); +} + +TEST(TestVertex, Test_ContainsAdjVertex) { + Vertex v1("A", {"B", "C", "E"}); + + ASSERT_EQ(v1.ContainsAdjVertex("C"), true); + + v1.DeleteAdjVertex("C"); + ASSERT_EQ(v1.ContainsAdjVertex("C"), false); + + v1.AddAdjVertex("F"); + ASSERT_EQ(v1.ContainsAdjVertex("F"), true); +} \ No newline at end of file diff --git a/sandbox/for_graph/src/weighted_graph_test.cpp b/sandbox/for_graph/src/weighted_graph_test.cpp new file mode 100644 index 0000000..2206120 --- /dev/null +++ b/sandbox/for_graph/src/weighted_graph_test.cpp @@ -0,0 +1,232 @@ +#include "graph/weighted_graph.hpp" + +#include + +TEST(Test_OrientedWeightedGraph, Test_Constructors) { + WeightedGraph wg1({{{'A', 'B'}, 2}, + {{'A', 'C'}, 5}, + {{'B', 'D'}, 3}, + {{'C', 'D'}, 7}, + {{'D', 'A'}, 1}}); + + WeightedGraph wg2( + {{'A', 'B'}, {'A', 'C'}, {'B', 'D'}, {'C', 'D'}, {'D', 'A'}}, + {2, 5, 3, 7, 1}); + + ASSERT_EQ(wg1.GetVerticesCount(), wg2.GetVerticesCount()); + ASSERT_EQ(wg1.GetEdgesCount(), wg2.GetEdgesCount()); + + auto edges = wg1.GetWeightedEdges(); + + for (auto edge : edges) { + ASSERT_TRUE(wg2.ContainsEdge(edge.start_vertex, edge.end_vertex)); + } +} + +TEST(Test_OrientedWeightedGraph, Test_AddWeightedEdge) { + WeightedGraph wg; + + wg.AddWeightedEdge('A', 'B', 3); + wg.AddWeightedEdge('A', 'C', 5); + wg.AddWeightedEdge('A', 'D', 2); + wg.AddWeightedEdge('B', 'C', 1); + wg.AddWeightedEdge('B', 'E', 4); + wg.AddWeightedEdge('C', 'E', 3); + wg.AddWeightedEdge('D', 'E', 6); + + ASSERT_TRUE(wg.ContainsEdge('A', 'B')); + ASSERT_TRUE(wg.ContainsEdge('A', 'C')); + ASSERT_TRUE(wg.ContainsEdge('A', 'D')); + ASSERT_TRUE(wg.ContainsEdge('B', 'C')); + ASSERT_TRUE(wg.ContainsEdge('B', 'E')); + ASSERT_TRUE(wg.ContainsEdge('C', 'E')); + ASSERT_TRUE(wg.ContainsEdge('D', 'E')); + + ASSERT_EQ(wg.GetEdgeWeight('A', 'B'), 3); + ASSERT_EQ(wg.GetEdgeWeight('A', 'C'), 5); + ASSERT_EQ(wg.GetEdgeWeight('A', 'D'), 2); + ASSERT_EQ(wg.GetEdgeWeight('B', 'C'), 1); + ASSERT_EQ(wg.GetEdgeWeight('B', 'E'), 4); + ASSERT_EQ(wg.GetEdgeWeight('C', 'E'), 3); + ASSERT_EQ(wg.GetEdgeWeight('D', 'E'), 6); +} + +TEST(Test_OrientedWeightedGraph, Test_DeleteVertex) { + WeightedGraph wg; + + wg.AddWeightedEdge('A', 'B', 3); + wg.AddWeightedEdge('A', 'C', 5); + wg.AddWeightedEdge('A', 'D', 2); + wg.AddWeightedEdge('B', 'C', 1); + wg.AddWeightedEdge('B', 'E', 4); + wg.AddWeightedEdge('C', 'E', 3); + wg.AddWeightedEdge('D', 'E', 6); + + wg.DeleteVertex('A'); + + ASSERT_FALSE(wg.ContainsEdge('A', 'B')); + ASSERT_FALSE(wg.ContainsEdge('A', 'C')); + ASSERT_FALSE(wg.ContainsEdge('A', 'D')); +} + +TEST(Test_OrientedWeightedGraph, Test_DeleteWeightedEdge) { + WeightedGraph wg; + + wg.AddWeightedEdge('A', 'B', 3); + wg.AddWeightedEdge('A', 'C', 5); + wg.AddWeightedEdge('A', 'D', 2); + wg.AddWeightedEdge('B', 'C', 1); + wg.AddWeightedEdge('B', 'E', 4); + wg.AddWeightedEdge('C', 'E', 3); + wg.AddWeightedEdge('D', 'E', 6); + + wg.DeleteWeightedEdge('A', 'B'); + + ASSERT_FALSE(wg.ContainsEdge('A', 'B')); + ASSERT_TRUE(wg.ContainsEdge('A', 'C')); + + ASSERT_THROW(wg.DeleteWeightedEdge('A', 'B'), std::invalid_argument); +} + +TEST(Test_OrientedWeightedGraph, Test_SetEdgeWeight) { + WeightedGraph wg; + + wg.AddWeightedEdge('A', 'B', 3); + wg.AddWeightedEdge('A', 'C', 5); + wg.AddWeightedEdge('A', 'D', 2); + wg.AddWeightedEdge('B', 'C', 1); + wg.AddWeightedEdge('B', 'E', 4); + wg.AddWeightedEdge('C', 'E', 3); + wg.AddWeightedEdge('D', 'E', 6); + + wg.SetEdgeWeight('A', 'B', 5); + + ASSERT_EQ(wg.GetEdgeWeight('A', 'B'), 5); + + ASSERT_THROW(wg.GetEdgeWeight('B', 'D'), std::invalid_argument); +} + +TEST(Test_NotOrientedWeightedGraph, Test_Constructors) { + WeightedGraph wg1({{{'A', 'B'}, 2}, + {{'A', 'C'}, 5}, + {{'B', 'D'}, 3}, + {{'C', 'D'}, 7}, + {{'D', 'A'}, 1}}, + false); + + WeightedGraph wg2( + {{'A', 'B'}, {'A', 'C'}, {'B', 'D'}, {'C', 'D'}, {'D', 'A'}}, + {2, 5, 3, 7, 1}, false); + + ASSERT_EQ(wg1.GetVerticesCount(), wg2.GetVerticesCount()); + ASSERT_EQ(wg1.GetEdgesCount(), wg2.GetEdgesCount()); + + auto edges = wg1.GetWeightedEdges(); + + for (auto edge : edges) { + ASSERT_TRUE(wg2.ContainsEdge(edge.start_vertex, edge.end_vertex)); + } +} + +TEST(Test_NotOrientedWeightedGraph, Test_AddWeightedEdge) { + WeightedGraph wg(false); + + wg.AddWeightedEdge('A', 'B', 3); + wg.AddWeightedEdge('A', 'C', 5); + wg.AddWeightedEdge('A', 'D', 2); + wg.AddWeightedEdge('B', 'C', 1); + wg.AddWeightedEdge('B', 'E', 4); + wg.AddWeightedEdge('C', 'E', 3); + wg.AddWeightedEdge('D', 'E', 6); + + ASSERT_TRUE(wg.ContainsEdge('A', 'B')); + ASSERT_TRUE(wg.ContainsEdge('B', 'A')); + ASSERT_TRUE(wg.ContainsEdge('A', 'C')); + ASSERT_TRUE(wg.ContainsEdge('C', 'A')); + ASSERT_TRUE(wg.ContainsEdge('A', 'D')); + ASSERT_TRUE(wg.ContainsEdge('D', 'A')); + ASSERT_TRUE(wg.ContainsEdge('B', 'C')); + ASSERT_TRUE(wg.ContainsEdge('C', 'B')); + ASSERT_TRUE(wg.ContainsEdge('B', 'E')); + ASSERT_TRUE(wg.ContainsEdge('E', 'B')); + ASSERT_TRUE(wg.ContainsEdge('C', 'E')); + ASSERT_TRUE(wg.ContainsEdge('E', 'C')); + ASSERT_TRUE(wg.ContainsEdge('D', 'E')); + ASSERT_TRUE(wg.ContainsEdge('E', 'D')); + + ASSERT_EQ(wg.GetEdgeWeight('A', 'B'), 3); + ASSERT_EQ(wg.GetEdgeWeight('B', 'A'), 3); + ASSERT_EQ(wg.GetEdgeWeight('A', 'C'), 5); + ASSERT_EQ(wg.GetEdgeWeight('C', 'A'), 5); + ASSERT_EQ(wg.GetEdgeWeight('A', 'D'), 2); + ASSERT_EQ(wg.GetEdgeWeight('D', 'A'), 2); + ASSERT_EQ(wg.GetEdgeWeight('B', 'C'), 1); + ASSERT_EQ(wg.GetEdgeWeight('C', 'B'), 1); + ASSERT_EQ(wg.GetEdgeWeight('B', 'E'), 4); + ASSERT_EQ(wg.GetEdgeWeight('E', 'B'), 4); + ASSERT_EQ(wg.GetEdgeWeight('C', 'E'), 3); + ASSERT_EQ(wg.GetEdgeWeight('E', 'C'), 3); + ASSERT_EQ(wg.GetEdgeWeight('D', 'E'), 6); + ASSERT_EQ(wg.GetEdgeWeight('E', 'D'), 6); +} + +TEST(Test_NotOrientedWeightedGraph, Test_DeleteVertex) { + WeightedGraph wg(false); + + wg.AddWeightedEdge('A', 'B', 3); + wg.AddWeightedEdge('A', 'C', 5); + wg.AddWeightedEdge('A', 'D', 2); + wg.AddWeightedEdge('B', 'C', 1); + wg.AddWeightedEdge('B', 'E', 4); + wg.AddWeightedEdge('C', 'E', 3); + wg.AddWeightedEdge('D', 'E', 6); + + wg.DeleteVertex('A'); + + ASSERT_FALSE(wg.ContainsEdge('A', 'B')); + ASSERT_FALSE(wg.ContainsEdge('B', 'A')); + ASSERT_FALSE(wg.ContainsEdge('A', 'C')); + ASSERT_FALSE(wg.ContainsEdge('C', 'A')); + ASSERT_FALSE(wg.ContainsEdge('A', 'D')); + ASSERT_FALSE(wg.ContainsEdge('D', 'A')); +} + +TEST(Test_NotOrientedWeightedGraph, Test_DeleteWeightedEdge) { + WeightedGraph wg(false); + + wg.AddWeightedEdge('A', 'B', 3); + wg.AddWeightedEdge('A', 'C', 5); + wg.AddWeightedEdge('A', 'D', 2); + wg.AddWeightedEdge('B', 'C', 1); + wg.AddWeightedEdge('B', 'E', 4); + wg.AddWeightedEdge('C', 'E', 3); + wg.AddWeightedEdge('D', 'E', 6); + + wg.DeleteWeightedEdge('A', 'B'); + + ASSERT_FALSE(wg.ContainsEdge('A', 'B')); + ASSERT_FALSE(wg.ContainsEdge('B', 'A')); + ASSERT_TRUE(wg.ContainsEdge('A', 'C')); + + ASSERT_THROW(wg.DeleteWeightedEdge('A', 'B'), std::invalid_argument); + ASSERT_THROW(wg.DeleteWeightedEdge('B', 'A'), std::invalid_argument); +} + +TEST(Test_NotOrientedWeightedGraph, Test_SetEdgeWeight) { + WeightedGraph wg(false); + + wg.AddWeightedEdge('A', 'B', 3); + wg.AddWeightedEdge('A', 'C', 5); + wg.AddWeightedEdge('A', 'D', 2); + wg.AddWeightedEdge('B', 'C', 1); + wg.AddWeightedEdge('B', 'E', 4); + wg.AddWeightedEdge('C', 'E', 3); + wg.AddWeightedEdge('D', 'E', 6); + + wg.SetEdgeWeight('A', 'B', 5); + + ASSERT_EQ(wg.GetEdgeWeight('A', 'B'), 5); + ASSERT_EQ(wg.GetEdgeWeight('B', 'A'), 5); + + ASSERT_THROW(wg.GetEdgeWeight('B', 'D'), std::invalid_argument); +} \ No newline at end of file diff --git a/task_01/src/test.cpp b/task_01/src/test.cpp index 87cef73..152d0cf 100644 --- a/task_01/src/test.cpp +++ b/task_01/src/test.cpp @@ -1,5 +1,311 @@ #include -TEST(Test, Simple) { - ASSERT_EQ(1, 1); // Stack [] +#include "topological_sort.hpp" + +TEST(Test_TopologicalSort, Simple_Test_1) { + Graph graph; + + auto result = TopologicalSort(graph); + std::vector expected; + + ASSERT_EQ(result, expected); +} + +TEST(Test_TopologicalSort, Simple_Test_2) { + Graph graph; + + graph.AddVertex('A'); + + auto result = TopologicalSort(graph); + std::vector expected{'A'}; + + ASSERT_EQ(result, expected); +} + +TEST(Test_TopologicalSort, Simple_Test_3) { + Graph graph; + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + + graph.AddEdge('A', 'B'); + graph.AddEdge('A', 'C'); + + auto result = TopologicalSort(graph); + std::vector expected{'A', 'C', 'B'}; + + ASSERT_EQ(result, expected); +} + +TEST(Test_TopologicalSort, Simple_Test_4) { + Graph graph; + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + + graph.AddEdge('A', 'C'); + graph.AddEdge('A', 'B'); + + auto result = TopologicalSort(graph); + std::vector expected{'A', 'B', 'C'}; + + ASSERT_EQ(result, expected); +} + +TEST(Test_TopologicalSort, Simple_Test_5) { + Graph graph; + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + + graph.AddEdge('A', 'B'); + graph.AddEdge('A', 'C'); + graph.AddEdge('C', 'B'); + + auto result = TopologicalSort(graph); + std::vector expected{'A', 'C', 'B'}; + + ASSERT_EQ(result, expected); +} + +TEST(Test_TopologicalSort, Test_1) { + Graph graph; + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + graph.AddVertex('D'); + graph.AddVertex('E'); + + graph.AddEdge('A', 'B'); + graph.AddEdge('A', 'C'); + graph.AddEdge('B', 'D'); + graph.AddEdge('C', 'E'); + + auto result = TopologicalSort(graph); + std::vector expected{'A', 'C', 'E', 'B', 'D'}; + + ASSERT_EQ(result, expected); +} + +TEST(Test_TopologicalSort, Test_2) { + Graph graph; + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + graph.AddVertex('D'); + graph.AddVertex('E'); + graph.AddVertex('F'); + graph.AddVertex('G'); + graph.AddVertex('H'); + graph.AddVertex('I'); + + graph.AddEdge('A', 'B'); + graph.AddEdge('A', 'C'); + graph.AddEdge('B', 'D'); + graph.AddEdge('C', 'E'); + + auto result = TopologicalSort(graph); + std::vector expected{'I', 'H', 'G', 'F', 'A', 'C', 'E', 'B', 'D'}; + + ASSERT_EQ(result, expected); +} + +TEST(Test_TopologicalSort, Test_3) { + Graph graph; + + graph.AddVertex("shirt"); + graph.AddVertex("coat"); + graph.AddVertex("blazer"); + graph.AddVertex("glasses"); + graph.AddVertex("pants"); + graph.AddVertex("shoes"); + graph.AddVertex("socks"); + + graph.AddEdge("shirt", "blazer"); + graph.AddEdge("shirt", "pants"); + graph.AddEdge("pants", "coat"); + graph.AddEdge("pants", "blazer"); + graph.AddEdge("blazer", "coat"); + graph.AddEdge("pants", "shoes"); + graph.AddEdge("socks", "shoes"); + + auto result = TopologicalSort(graph); + std::vector expected{"socks", "glasses", "shirt", "pants", + "shoes", "blazer", "coat"}; + + ASSERT_EQ(result, expected); +} + +TEST(Test_TopologicalSort, Test_4) { + Graph graph; + + graph.AddVertex(1); + graph.AddVertex(2); + graph.AddVertex(3); + graph.AddVertex(4); + graph.AddVertex(5); + graph.AddVertex(6); + graph.AddVertex(7); + graph.AddVertex(8); + graph.AddVertex(9); + graph.AddVertex(10); + + graph.AddEdge(1, 10); + graph.AddEdge(1, 6); + graph.AddEdge(10, 3); + graph.AddEdge(10, 5); + graph.AddEdge(6, 5); + graph.AddEdge(6, 2); + graph.AddEdge(3, 8); + graph.AddEdge(3, 5); + graph.AddEdge(5, 8); + graph.AddEdge(5, 4); + graph.AddEdge(5, 2); + graph.AddEdge(2, 4); + graph.AddEdge(2, 7); + graph.AddEdge(4, 8); + graph.AddEdge(9, 8); + + auto result = TopologicalSort(graph); + std::vector expected{9, 1, 6, 10, 3, 5, 2, 7, 4, 8}; + + ASSERT_EQ(result, expected); +} + +TEST(Test_TopologicalSort, Test_5) { + Graph graph; + + graph.AddVertex('H'); + graph.AddVertex('E'); + graph.AddVertex('F'); + graph.AddVertex('C'); + graph.AddVertex('G'); + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('D'); + + graph.AddEdge('H', 'F'); + graph.AddEdge('F', 'C'); + graph.AddEdge('G', 'A'); + graph.AddEdge('H', 'D'); + graph.AddEdge('A', 'H'); + graph.AddEdge('F', 'B'); + graph.AddEdge('A', 'F'); + graph.AddEdge('D', 'F'); + + auto result = TopologicalSort(graph); + std::vector expected{'G', 'A', 'E', 'H', 'D', 'F', 'B', 'C'}; + + ASSERT_EQ(result, expected); +} + +TEST(Test_TopologicalSort, Test_6) { + Graph graph; + graph.AddVertex("A"); + graph.AddVertex("B"); + graph.AddVertex("C"); + graph.AddVertex("D"); + graph.AddVertex("E"); + + graph.AddEdge("A", "C"); + graph.AddEdge("B", "C"); + graph.AddEdge("C", "D"); + graph.AddEdge("C", "E"); + + auto result = TopologicalSort(graph); + std::vector expected = {"B", "A", "C", "E", "D"}; + + ASSERT_EQ(result, expected); +} + +TEST(Test_TopologicalSort, Test_7) { + Graph graph; + graph.AddVertex("A"); + graph.AddVertex("B"); + graph.AddVertex("C"); + graph.AddVertex("D"); + graph.AddVertex("E"); + + graph.AddEdge("A", "B"); + graph.AddEdge("C", "D"); + + auto result = TopologicalSort(graph); + std::vector expected = {"E", "C", "D", "A", "B"}; + + ASSERT_EQ(result, expected); +} + +TEST(Test_TopologicalSort, Test_NotAcyclic_1) { + Graph graph; + + graph.AddVertex(1); + graph.AddVertex(2); + graph.AddVertex(3); + + graph.AddEdge(1, 2); + graph.AddEdge(2, 3); + graph.AddEdge(3, 1); + + ASSERT_THROW(TopologicalSort(graph), std::invalid_argument); +} + +TEST(Test_TopologicalSort, Test_NotAcyclic_2) { + Graph graph; + + graph.AddVertex('G'); + graph.AddVertex('D'); + graph.AddVertex('B'); + graph.AddVertex('C'); + graph.AddVertex('F'); + graph.AddVertex('E'); + graph.AddVertex('H'); + graph.AddVertex('A'); + + graph.AddEdge('E', 'B'); + graph.AddEdge('A', 'G'); + graph.AddEdge('G', 'F'); + graph.AddEdge('D', 'H'); + graph.AddEdge('H', 'G'); + graph.AddEdge('B', 'F'); + graph.AddEdge('G', 'E'); + graph.AddEdge('F', 'D'); + + ASSERT_THROW(TopologicalSort(graph), std::invalid_argument); +} + +TEST(Test_TopologicalSort, Test_NotAcyclic_3) { + Graph graph; + + graph.AddVertex(1); + graph.AddVertex(2); + graph.AddVertex(3); + graph.AddVertex(4); + graph.AddVertex(5); + graph.AddVertex(6); + graph.AddVertex(7); + graph.AddVertex(8); + graph.AddVertex(9); + graph.AddVertex(10); + graph.AddVertex(11); + + graph.AddEdge(1, 10); + graph.AddEdge(10, 11); + graph.AddEdge(5, 1); + graph.AddEdge(5, 6); + graph.AddEdge(4, 8); + graph.AddEdge(2, 3); + graph.AddEdge(10, 9); + graph.AddEdge(4, 5); + graph.AddEdge(5, 7); + graph.AddEdge(4, 7); + graph.AddEdge(1, 2); + graph.AddEdge(3, 4); + graph.AddEdge(1, 6); + + ASSERT_THROW(TopologicalSort(graph), std::invalid_argument); } \ No newline at end of file diff --git a/task_01/src/topological_sort.hpp b/task_01/src/topological_sort.hpp new file mode 100644 index 0000000..9af18d5 --- /dev/null +++ b/task_01/src/topological_sort.hpp @@ -0,0 +1,31 @@ +#include + +#include "graph/graph.hpp" + +template +void DFS(Graph& graph, std::unordered_map& visited, + std::vector& res, const T& curr_vertex) { + if (visited[curr_vertex]) + throw std::invalid_argument("Graph is not acyclic!"); + + visited[curr_vertex] = true; + + for (auto adj_vertex : graph.GetAdjVertices(curr_vertex)) + if (graph.ContainsVertex(adj_vertex)) DFS(graph, visited, res, adj_vertex); + + graph.DeleteVertex(curr_vertex); + res.push_back(curr_vertex); +} + +template +std::vector TopologicalSort(Graph graph) { + std::vector reversed_res; + std::unordered_map visited; + + while (graph.GetVerticesCount()) + DFS(graph, visited, reversed_res, graph.GetVerticesIds()[0]); + + reverse(reversed_res.begin(), reversed_res.end()); + + return reversed_res; +} \ No newline at end of file diff --git a/task_02/src/find_bridges.hpp b/task_02/src/find_bridges.hpp new file mode 100644 index 0000000..aa9ce71 --- /dev/null +++ b/task_02/src/find_bridges.hpp @@ -0,0 +1,56 @@ +#include + +#include "graph/graph.hpp" + +template +struct Edge { + public: + T v1; + T v2; +}; + +template +bool operator==(const Edge& lhs, const Edge& rhs) { + return (lhs.v1 == rhs.v1 && lhs.v2 == rhs.v2) || + (lhs.v1 == rhs.v2 && lhs.v2 == rhs.v1); +} + +template +void DFSForBridges(std::unordered_map& d, std::unordered_map& h, + std::unordered_map& visited, + std::vector>& bridges, Graph& graph, const T& v, + const T& p = T()) { + visited[v] = true; + if (p) d[v] = h[v] = h[p] + 1; + + for (auto u : graph.GetAdjVertices(v)) { + if (u != p) { + if (visited[u]) + d[v] = std::min(d[v], h[u]); + else { + DFSForBridges(d, h, visited, bridges, graph, u, v); + + d[v] = std::min(d[v], d[u]); + if (h[v] < d[u]) bridges.push_back({v, u}); + } + } + } +} + +template +std::vector> FindBridges(Graph graph) { + if (graph.IsOriented()) throw std::invalid_argument("Graph is oriented!"); + + std::unordered_map d, h; + std::unordered_map visited; + for (auto v : graph.GetVerticesIds()) { + d[v] = h[v] = 0; + visited[v] = false; + } + + std::vector> bridges; + if (graph.GetVerticesCount()) + DFSForBridges(d, h, visited, bridges, graph, graph.GetVerticesIds()[0]); + + return bridges; +} \ No newline at end of file diff --git a/task_02/src/find_joint_vertices.hpp b/task_02/src/find_joint_vertices.hpp new file mode 100644 index 0000000..88c4574 --- /dev/null +++ b/task_02/src/find_joint_vertices.hpp @@ -0,0 +1,55 @@ +#include +#include + +#include "graph/graph.hpp" + +template +void DFSForJointVertices(std::unordered_map& d, + std::unordered_map& h, + std::unordered_map& visited, + std::vector& joint_vertices, Graph& graph, + const T& v, const T& p = T()) { + visited[v] = true; + if (p) d[v] = h[v] = h[p] + 1; + + int children_count = 0; + + for (auto u : graph.GetAdjVertices(v)) { + if (u != p) { + if (visited[u]) + d[v] = std::min(d[v], h[u]); + else { + DFSForJointVertices(d, h, visited, joint_vertices, graph, u, v); + + d[v] = std::min(d[v], d[u]); + if (h[v] <= d[u] && p) + if (std::find(joint_vertices.begin(), joint_vertices.end(), v) == + joint_vertices.end()) + joint_vertices.push_back(v); + + children_count++; + } + } + } + // Отдельно проверяем корень + if (!p && children_count > 1) joint_vertices.push_back(v); +} + +template +std::vector FindJointVertices(Graph graph) { + if (graph.IsOriented()) throw std::invalid_argument("Graph is oriented!"); + + std::unordered_map d, h; + std::unordered_map visited; + for (auto v : graph.GetVerticesIds()) { + d[v] = h[v] = 0; + visited[v] = false; + } + + std::vector joint_vertices; + if (graph.GetVerticesCount()) + DFSForJointVertices(d, h, visited, joint_vertices, graph, + graph.GetVerticesIds()[0]); + + return joint_vertices; +} \ No newline at end of file diff --git a/task_02/src/main.cpp b/task_02/src/main.cpp index 0e4393b..76e8197 100644 --- a/task_02/src/main.cpp +++ b/task_02/src/main.cpp @@ -1,3 +1 @@ -#include - int main() { return 0; } diff --git a/task_02/src/stack.cpp b/task_02/src/stack.cpp deleted file mode 100644 index 8ca8990..0000000 --- a/task_02/src/stack.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "stack.hpp" - -#include - -void Stack::Push(int value) { data_.push(value); } - -int Stack::Pop() { - auto result = data_.top(); - data_.pop(); - return result; -} - -void MinStack::Push(int value) { data_.push_back(value); } - -int MinStack::Pop() { - auto result = data_.back(); - data_.pop_back(); - return result; -} - -int MinStack::GetMin() { return *std::min_element(data_.begin(), data_.end()); } \ No newline at end of file diff --git a/task_02/src/stack.hpp b/task_02/src/stack.hpp deleted file mode 100644 index 138ec40..0000000 --- a/task_02/src/stack.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include -#include - -class Stack { - public: - void Push(int value); - int Pop(); - - private: - std::stack data_; -}; - -class MinStack { - public: - void Push(int value); - int Pop(); - int GetMin(); - - private: - std::vector data_; -}; diff --git a/task_02/src/test.cpp b/task_02/src/test.cpp index 54e7ce9..57e065d 100644 --- a/task_02/src/test.cpp +++ b/task_02/src/test.cpp @@ -1,42 +1,422 @@ - #include -#include - -#include "stack.hpp" - -TEST(StackTest, Simple) { - Stack stack; - stack.Push(1); // Stack [1] - ASSERT_EQ(stack.Pop(), 1); // Stack [] - stack.Push(1); // Stack [1] - stack.Push(2); // Stack [1, 2] - ASSERT_EQ(stack.Pop(), 2); // Stack [1] - ASSERT_EQ(stack.Pop(), 1); // Stack [] - stack.Push(1); // Stack [1] - stack.Push(2); // Stack [1, 2] - ASSERT_EQ(stack.Pop(), 2); // Stack [1] - stack.Push(3); // Stack [1, 3] - ASSERT_EQ(stack.Pop(), 3); // Stack [1] - ASSERT_EQ(stack.Pop(), 1); // Stack [] -} - -TEST(MinStackTest, Simple) { - MinStack stack; - stack.Push(1); // Stack [1] - ASSERT_EQ(stack.GetMin(), 1); - ASSERT_EQ(stack.Pop(), 1); // Stack [] - stack.Push(1); // Stack [1] - stack.Push(2); // Stack [1, 2] - ASSERT_EQ(stack.GetMin(), 1); - ASSERT_EQ(stack.Pop(), 2); // Stack [1] - ASSERT_EQ(stack.Pop(), 1); // Stack [] - stack.Push(1); // Stack [1] - stack.Push(2); // Stack [1, 2] - ASSERT_EQ(stack.GetMin(), 1); - ASSERT_EQ(stack.Pop(), 2); // Stack [1] - stack.Push(3); // Stack [1, 3] - ASSERT_EQ(stack.GetMin(), 1); - ASSERT_EQ(stack.Pop(), 3); // Stack [1] - ASSERT_EQ(stack.Pop(), 1); // Stack [] +#include "find_bridges.hpp" +#include "find_joint_vertices.hpp" + +TEST(Test_FindBridges, Simple_Test_1) { + Graph graph(false); + + auto result = FindBridges(graph); + std::vector> expected; + + ASSERT_EQ(result, expected); +} + +TEST(Test_FindBridges, Simple_Test_2) { + Graph graph(false); + + graph.AddVertex('A'); + + auto result = FindBridges(graph); + std::vector> expected; + + ASSERT_EQ(result, expected); +} + +TEST(Test_FindBridges, Simple_Test_3) { + Graph graph(false); + + graph.AddVertex('A'); + graph.AddVertex('B'); + + graph.AddEdge('A', 'B'); + + auto result = FindBridges(graph); + std::vector> expected{{'A', 'B'}}; + + ASSERT_EQ(result, expected); +} + +TEST(Test_FindBridges, Simple_Test_4) { + Graph graph(false); + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + + graph.AddEdge('A', 'B'); + graph.AddEdge('A', 'C'); + graph.AddEdge('B', 'C'); + + auto result = FindBridges(graph); + std::vector> expected; + + ASSERT_EQ(result, expected); +} + +TEST(Test_FindBridges, Simple_Test_5) { + Graph graph(false); + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + + graph.AddEdge('A', 'B'); + graph.AddEdge('A', 'C'); + + auto result = FindBridges(graph); + std::vector> expected{{'A', 'B'}, {'A', 'C'}}; + + ASSERT_EQ(result, expected); +} + +TEST(Test_FindBridges, Test_1) { + Graph graph(false); + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + graph.AddVertex('D'); + graph.AddVertex('E'); + graph.AddVertex('F'); + graph.AddVertex('G'); + + graph.AddEdge('A', 'B'); + graph.AddEdge('A', 'C'); + graph.AddEdge('C', 'D'); + graph.AddEdge('C', 'E'); + graph.AddEdge('D', 'E'); + graph.AddEdge('B', 'G'); + graph.AddEdge('B', 'F'); + + auto result = FindBridges(graph); + std::vector> expected{ + {'B', 'G'}, {'B', 'F'}, {'A', 'B'}, {'A', 'C'}}; + + ASSERT_EQ(result, expected); +} + +TEST(Test_FindBridges, Test_2) { + Graph graph(false); + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + graph.AddVertex('D'); + graph.AddVertex('E'); + graph.AddVertex('F'); + graph.AddVertex('G'); + + graph.AddEdge('A', 'B'); + graph.AddEdge('A', 'C'); + graph.AddEdge('C', 'D'); + graph.AddEdge('C', 'E'); + graph.AddEdge('D', 'E'); + graph.AddEdge('B', 'G'); + graph.AddEdge('B', 'F'); + graph.AddEdge('G', 'F'); + graph.AddEdge('D', 'F'); + + auto result = FindBridges(graph); + std::vector> expected; + + ASSERT_EQ(result, expected); +} + +TEST(Test_FindBridges, Test_3) { + Graph graph(false); + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + graph.AddVertex('D'); + graph.AddVertex('E'); + graph.AddVertex('F'); + graph.AddVertex('G'); + graph.AddVertex('H'); + graph.AddVertex('I'); + graph.AddVertex('J'); + graph.AddVertex('K'); + graph.AddVertex('L'); + graph.AddVertex('M'); + + graph.AddEdge('F', 'J'); + graph.AddEdge('F', 'D'); + graph.AddEdge('J', 'I'); + graph.AddEdge('J', 'A'); + graph.AddEdge('J', 'K'); + graph.AddEdge('I', 'M'); + graph.AddEdge('I', 'E'); + graph.AddEdge('A', 'H'); + graph.AddEdge('K', 'H'); + graph.AddEdge('K', 'G'); + graph.AddEdge('K', 'D'); + graph.AddEdge('D', 'C'); + graph.AddEdge('C', 'B'); + graph.AddEdge('B', 'L'); + + auto result = FindBridges(graph); + std::vector> expected{ + {'K', 'G'}, {'B', 'L'}, {'C', 'B'}, {'D', 'C'}, + {'I', 'M'}, {'I', 'E'}, {'J', 'I'}, + }; + + ASSERT_EQ(result, expected); +} + +TEST(Test_FindBridges, Test_4) { + Graph graph(false); + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + graph.AddVertex('D'); + graph.AddVertex('E'); + + graph.AddEdge('A', 'D'); + graph.AddEdge('A', 'C'); + graph.AddEdge('C', 'D'); + graph.AddEdge('E', 'D'); + graph.AddEdge('B', 'D'); + graph.AddEdge('E', 'B'); + + auto result = FindBridges(graph); + std::vector> expected; + + ASSERT_EQ(result, expected); +} + +TEST(Test_FindBridges, Test_5) { + Graph graph(false); + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + graph.AddVertex('D'); + graph.AddVertex('E'); + graph.AddVertex('F'); + + graph.AddEdge('A', 'B'); + graph.AddEdge('A', 'C'); + graph.AddEdge('B', 'C'); + graph.AddEdge('C', 'D'); + graph.AddEdge('C', 'E'); + graph.AddEdge('E', 'F'); + + auto result = FindBridges(graph); + std::vector> expected{{'C', 'D'}, {'E', 'F'}, {'C', 'E'}}; + + ASSERT_EQ(result, expected); +} + +TEST(Test_FindJointVertices, Simple_Test_1) { + Graph graph(false); + + auto result = FindJointVertices(graph); + std::vector expected; + + ASSERT_EQ(result, expected); +} + +TEST(Test_FindJointVertices, Simple_Test_2) { + Graph graph(false); + + graph.AddVertex('A'); + + auto result = FindJointVertices(graph); + std::vector expected; + + ASSERT_EQ(result, expected); +} + +TEST(Test_FindJointVertices, Simple_Test_3) { + Graph graph(false); + + graph.AddVertex('A'); + graph.AddVertex('B'); + + graph.AddEdge('A', 'B'); + + auto result = FindJointVertices(graph); + std::vector expected; + + ASSERT_EQ(result, expected); +} + +TEST(Test_FindJointVertices, Simple_Test_4) { + Graph graph(false); + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + + graph.AddEdge('A', 'B'); + graph.AddEdge('A', 'C'); + graph.AddEdge('B', 'C'); + + auto result = FindJointVertices(graph); + std::vector expected; + + ASSERT_EQ(result, expected); +} + +TEST(Test_FindJointVertices, Simple_Test_5) { + Graph graph(false); + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + + graph.AddEdge('A', 'B'); + graph.AddEdge('A', 'C'); + + auto result = FindJointVertices(graph); + std::vector expected{'A'}; + + ASSERT_EQ(result, expected); +} + +TEST(Test_FindJointVertices, Test_1) { + Graph graph(false); + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + graph.AddVertex('D'); + graph.AddVertex('E'); + graph.AddVertex('F'); + graph.AddVertex('G'); + + graph.AddEdge('A', 'B'); + graph.AddEdge('A', 'C'); + graph.AddEdge('C', 'D'); + graph.AddEdge('C', 'E'); + graph.AddEdge('D', 'E'); + graph.AddEdge('B', 'G'); + graph.AddEdge('B', 'F'); + + auto result = FindJointVertices(graph); + std::vector expected{'B', 'C', 'A'}; + + ASSERT_EQ(result, expected); +} + +TEST(Test_FindJointVertices, Test_2) { + Graph graph(false); + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + graph.AddVertex('D'); + graph.AddVertex('E'); + graph.AddVertex('F'); + graph.AddVertex('G'); + + graph.AddEdge('A', 'B'); + graph.AddEdge('A', 'C'); + graph.AddEdge('C', 'D'); + graph.AddEdge('C', 'E'); + graph.AddEdge('D', 'E'); + graph.AddEdge('B', 'G'); + graph.AddEdge('B', 'F'); + graph.AddEdge('G', 'F'); + graph.AddEdge('D', 'F'); + + auto result = FindJointVertices(graph); + std::vector expected; + + ASSERT_EQ(result, expected); +} + +TEST(Test_FindJointVertices, Test_3) { + Graph graph(false); + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + graph.AddVertex('D'); + graph.AddVertex('E'); + graph.AddVertex('F'); + graph.AddVertex('G'); + graph.AddVertex('H'); + graph.AddVertex('I'); + graph.AddVertex('J'); + graph.AddVertex('K'); + graph.AddVertex('L'); + graph.AddVertex('M'); + + graph.AddEdge('F', 'J'); + graph.AddEdge('F', 'D'); + graph.AddEdge('J', 'I'); + graph.AddEdge('J', 'A'); + graph.AddEdge('J', 'K'); + graph.AddEdge('I', 'M'); + graph.AddEdge('I', 'E'); + graph.AddEdge('A', 'H'); + graph.AddEdge('K', 'H'); + graph.AddEdge('K', 'G'); + graph.AddEdge('K', 'D'); + graph.AddEdge('D', 'C'); + graph.AddEdge('C', 'B'); + graph.AddEdge('B', 'L'); + + auto result = FindJointVertices(graph); + std::vector expected{'K', 'B', 'C', 'D', 'I', 'J'}; + + ASSERT_EQ(result, expected); +} + +TEST(Test_FindJointVertices, Test_4) { + Graph graph(false); + + graph.AddVertex(0); + graph.AddVertex(1); + graph.AddVertex(2); + graph.AddVertex(3); + graph.AddVertex(4); + graph.AddVertex(5); + graph.AddVertex(6); + graph.AddVertex(7); + graph.AddVertex(8); + graph.AddVertex(9); + + graph.AddEdge(0, 1); + graph.AddEdge(0, 2); + graph.AddEdge(1, 2); + graph.AddEdge(1, 3); + graph.AddEdge(2, 4); + graph.AddEdge(3, 4); + graph.AddEdge(3, 5); + graph.AddEdge(4, 6); + graph.AddEdge(5, 7); + graph.AddEdge(6, 8); + graph.AddEdge(7, 8); + graph.AddEdge(8, 9); + + auto result = FindJointVertices(graph); + std::vector expected{8}; + + ASSERT_EQ(result, expected); +} + +TEST(Test_FindJointVertices, Test_5) { + Graph graph(false); + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + graph.AddVertex('D'); + graph.AddVertex('E'); + + graph.AddEdge('A', 'D'); + graph.AddEdge('A', 'C'); + graph.AddEdge('C', 'D'); + graph.AddEdge('E', 'D'); + graph.AddEdge('B', 'D'); + graph.AddEdge('E', 'B'); + + auto result = FindJointVertices(graph); + std::vector expected{'D'}; + + ASSERT_EQ(result, expected); } \ No newline at end of file diff --git a/task_03/CMakeLists.txt b/task_03/CMakeLists.txt index 7c1cb30..65417b5 100644 --- a/task_03/CMakeLists.txt +++ b/task_03/CMakeLists.txt @@ -25,7 +25,8 @@ find_package(GTest REQUIRED) include_directories(${GTEST_INCLUDE_DIRS}) find_library(Utils ../) -target_link_libraries(${PROJECT_NAME} PUBLIC Utils) +find_library(Dijkstra ../) +target_link_libraries(${PROJECT_NAME} PUBLIC Utils Dijkstra) # Link runTests with what we want to test and the GTest and pthread library add_executable(${PROJECT_NAME}_tests ${test_source_list}) @@ -33,6 +34,7 @@ target_link_libraries( ${PROJECT_NAME}_tests GTest::gtest_main Utils + Dijkstra ) include(GoogleTest) diff --git a/task_03/src/bellman_ford.hpp b/task_03/src/bellman_ford.hpp new file mode 100644 index 0000000..50ec9e6 --- /dev/null +++ b/task_03/src/bellman_ford.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include + +#include "dijkstra.hpp" + +template +void BellmanFordStep(WeightedGraph& graph, + std::map>& min_paths, bool& is_updated) { + for (auto w_edge : graph.GetWeightedEdges()) { + T v = w_edge.start_vertex; + T u = w_edge.end_vertex; + + if (min_paths[v].weight != std::numeric_limits::max() && + min_paths[u].weight > min_paths[v].weight + w_edge.weight) { + is_updated = true; + + min_paths[u].weight = min_paths[v].weight + w_edge.weight; + // Обновляем кратчайший путь + min_paths[u].vertices = min_paths[v].vertices; + min_paths[u].vertices.push_back(v); + } + } +} + +template +std::vector> BellmanFord(const T& vertex, WeightedGraph graph) { + if (!graph.ContainsVertex(vertex)) + throw std::invalid_argument("Root vertex not found!"); + + // Использован map для того, чтобы были отсортированы ключи (легче писать + // тесты) + std::map> min_paths; + for (auto v : graph.GetVerticesIds()) { + if (v == vertex) + min_paths[v].weight = 0; + else + min_paths[v].weight = std::numeric_limits::max(); + } + + bool is_updated = false; + for (int i = 0; i < graph.GetVerticesCount() - 1; i++) { + is_updated = false; + BellmanFordStep(graph, min_paths, is_updated); + if (!is_updated) break; + } + + // Проверяем на цикл с отрицательным весом + is_updated = false; + BellmanFordStep(graph, min_paths, is_updated); + if (is_updated) + throw std::invalid_argument("Graph has cicle with negative weight!"); + + // Удаляем вершину, для которой мы находили кратчайшие пути до других вершин + min_paths.erase(vertex); + + std::vector> paths; + for (auto [v, min_path] : min_paths) { + min_path.vertices.push_back(v); + paths.push_back(min_path); + } + + return paths; +} \ No newline at end of file diff --git a/task_03/src/johnson.hpp b/task_03/src/johnson.hpp new file mode 100644 index 0000000..1daeb38 --- /dev/null +++ b/task_03/src/johnson.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include "bellman_ford.hpp" +#include "dijkstra.hpp" + +template +std::map> Johnson(WeightedGraph graph) { + std::unordered_map h; + for (T v : graph.GetVerticesIds()) h[v] = 0; + + // Добавляем новую вершину и ребра до каждой вершины из новой с весом 0 + T new_vertex; + graph.AddVertex(new_vertex); + for (T v : graph.GetVerticesIds()) + if (v != new_vertex) graph.AddWeightedEdge(new_vertex, v); + + // Вызываем алгоритм Беллмана-Форда для новой вершины + auto min_paths_bellman_ford = BellmanFord(new_vertex, graph); + + for (auto min_path : min_paths_bellman_ford) { + T v = min_path.vertices[min_path.vertices.size() - 1]; + h[v] = min_path.weight; + } + graph.DeleteVertex(new_vertex); + + // Перевзвешиваем все ребра, чтобы вес каждого ребра был положительным + for (WeightedEdge w_edge : graph.GetWeightedEdges()) { + T v = w_edge.start_vertex; + T u = w_edge.end_vertex; + graph.SetEdgeWeight(v, u, w_edge.weight + h[v] - h[u]); + } + + std::map> res; + + for (T v : graph.GetVerticesIds()) { + // Вызываем алгоритм Дейкстры для каждой вершины + auto min_paths_dijkstra = Dijkstra(v, graph); + + for (auto min_path : min_paths_dijkstra) { + auto path = min_path.vertices; + T u = path[path.size() - 1]; + // Возвращаем истинный вес минимального пути + if (min_path.weight != INF) + res[v][u] = min_path.weight + h[u] - h[v]; + else + res[v][u] = INF; + } + res[v][v] = 0; + } + + return res; +} diff --git a/task_03/src/main.cpp b/task_03/src/main.cpp index 0e4393b..76e8197 100644 --- a/task_03/src/main.cpp +++ b/task_03/src/main.cpp @@ -1,3 +1 @@ -#include - int main() { return 0; } diff --git a/task_03/src/test.cpp b/task_03/src/test.cpp index ef5a86a..d1df90a 100644 --- a/task_03/src/test.cpp +++ b/task_03/src/test.cpp @@ -1,8 +1,603 @@ - #include -#include "topology_sort.hpp" +#include "bellman_ford.hpp" +#include "johnson.hpp" + +template +bool operator==(std::map lhs, std::map rhs) { + if (lhs.size() != rhs.size()) return false; + + for (auto [v, verts] : lhs) + if (lhs[v] != rhs[v]) return false; + + return true; +} + +TEST(Test_BellmanFord, Simple_Test_1) { + WeightedGraph graph; + + ASSERT_THROW(BellmanFord('A', graph), std::invalid_argument); +} + +TEST(Test_BellmanFord, Simple_Test_2) { + WeightedGraph graph; + + graph.AddVertex('A'); + + auto result = BellmanFord('A', graph); + std::vector> expected; + + ASSERT_EQ(result, expected); +} + +TEST(Test_BellmanFord, Simple_Test_3) { + WeightedGraph graph; + + graph.AddVertex('A'); + graph.AddVertex('B'); + + graph.AddWeightedEdge('A', 'B', -1); + graph.AddWeightedEdge('B', 'A', -1); + + ASSERT_THROW(BellmanFord('A', graph), std::invalid_argument); +} + +TEST(Test_BellmanFord, Simple_Test_4) { + WeightedGraph graph; + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + + graph.AddWeightedEdge('A', 'B', -1); + graph.AddWeightedEdge('A', 'C', 2); + graph.AddWeightedEdge('B', 'C', 2); + + auto result = BellmanFord('A', graph); + std::vector> expected; + + expected.push_back(MinPath({'A', 'B'}, -1)); + expected.push_back(MinPath({'A', 'B', 'C'}, 1)); + + ASSERT_EQ(result, expected); +} + +TEST(Test_BellmanFord, Simple_Test_5) { + WeightedGraph graph(false); + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + + graph.AddWeightedEdge('A', 'B', 1); + graph.AddWeightedEdge('A', 'C', 4); + graph.AddWeightedEdge('B', 'C', 2); + + auto result = BellmanFord('A', graph); + std::vector> expected; + + expected.push_back(MinPath({'A', 'B'}, 1)); + expected.push_back(MinPath({'A', 'B', 'C'}, 3)); + + ASSERT_EQ(result, expected); +} + +TEST(Test_BellmanFord, Simple_Test_6) { + WeightedGraph graph(false); + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + + graph.AddWeightedEdge('A', 'B', -1); + graph.AddWeightedEdge('A', 'C', 2); + graph.AddWeightedEdge('B', 'C', 2); + + ASSERT_THROW(BellmanFord('A', graph), std::invalid_argument); +} + +TEST(Test_BellmanFord, Simple_Test_7) { + WeightedGraph graph; + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('S'); + + graph.AddWeightedEdge('A', 'B', -1); + graph.AddWeightedEdge('B', 'A', 2); + graph.AddWeightedEdge('S', 'A', 0); + graph.AddWeightedEdge('S', 'B', 0); + + auto result = BellmanFord('S', graph); + std::vector> expected; + + expected.push_back(MinPath({'S', 'A'}, 0)); + expected.push_back(MinPath({'S', 'A', 'B'}, -1)); + + ASSERT_EQ(result, expected); +} + +TEST(Test_BellmanFord, Test_1) { + WeightedGraph graph; + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + graph.AddVertex('D'); + graph.AddVertex('E'); + + graph.AddWeightedEdge('A', 'B', 2); + graph.AddWeightedEdge('A', 'C', -1); + graph.AddWeightedEdge('B', 'C', 1); + graph.AddWeightedEdge('B', 'D', 3); + graph.AddWeightedEdge('C', 'D', 4); + graph.AddWeightedEdge('C', 'E', 2); + graph.AddWeightedEdge('D', 'E', -3); + + auto result = BellmanFord('A', graph); + std::vector> expected; + + expected.push_back(MinPath({'A', 'B'}, 2)); + expected.push_back(MinPath({'A', 'C'}, -1)); + expected.push_back(MinPath({'A', 'C', 'D'}, 3)); + expected.push_back(MinPath({'A', 'C', 'D', 'E'}, 0)); + + ASSERT_EQ(result, expected); +} + +TEST(Test_BellmanFord, Test_2) { + WeightedGraph graph; + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + graph.AddVertex('D'); + graph.AddVertex('E'); + graph.AddVertex('F'); + graph.AddVertex('G'); + + graph.AddWeightedEdge('A', 'B', 5); + graph.AddWeightedEdge('A', 'C', 2); + graph.AddWeightedEdge('B', 'C', 1); + graph.AddWeightedEdge('B', 'D', 3); + graph.AddWeightedEdge('C', 'E', -2); + graph.AddWeightedEdge('C', 'F', 4); + graph.AddWeightedEdge('D', 'E', 2); + graph.AddWeightedEdge('D', 'G', 6); + graph.AddWeightedEdge('E', 'F', 3); + graph.AddWeightedEdge('E', 'G', -4); + graph.AddWeightedEdge('F', 'G', 1); + graph.AddWeightedEdge('F', 'C', -7); + graph.AddWeightedEdge('G', 'A', 8); + + ASSERT_THROW(BellmanFord('A', graph), std::invalid_argument); +} + +TEST(Test_BellmanFord, Test_3) { + WeightedGraph graph; + + graph.AddVertex(0); + graph.AddVertex(1); + graph.AddVertex(2); + graph.AddVertex(3); + graph.AddVertex(4); + graph.AddVertex(5); + graph.AddVertex(6); + graph.AddVertex(7); + graph.AddVertex(8); + graph.AddVertex(9); + graph.AddVertex(10); + graph.AddVertex(11); + + graph.AddWeightedEdge(0, 1, 1); + graph.AddWeightedEdge(1, 2, 4); + graph.AddWeightedEdge(2, 3, 5); + graph.AddWeightedEdge(4, 0, 7); + graph.AddWeightedEdge(5, 1, 2); + graph.AddWeightedEdge(6, 2, 3); + graph.AddWeightedEdge(3, 7, -3); + graph.AddWeightedEdge(5, 4, 5); + graph.AddWeightedEdge(5, 6, -2); + graph.AddWeightedEdge(6, 7, 7); + graph.AddWeightedEdge(8, 4, -6); + graph.AddWeightedEdge(5, 9, 4); + graph.AddWeightedEdge(6, 10, 6); + graph.AddWeightedEdge(7, 11, 4); + graph.AddWeightedEdge(9, 8, 3); + graph.AddWeightedEdge(9, 10, 1); + graph.AddWeightedEdge(10, 11, 2); + + auto result = BellmanFord(5, graph); + std::vector> expected; + + expected.push_back(MinPath({5, 9, 8, 4, 0}, 8)); + expected.push_back(MinPath({5, 1}, 2)); + expected.push_back(MinPath({5, 6, 2}, 1)); + expected.push_back(MinPath({5, 6, 2, 3}, 6)); + expected.push_back(MinPath({5, 9, 8, 4}, 1)); + expected.push_back(MinPath({5, 6}, -2)); + expected.push_back(MinPath({5, 6, 2, 3, 7}, 3)); + expected.push_back(MinPath({5, 9, 8}, 7)); + expected.push_back(MinPath({5, 9}, 4)); + expected.push_back(MinPath({5, 6, 10}, 4)); + expected.push_back(MinPath({5, 6, 10, 11}, 6)); + + ASSERT_EQ(result, expected); +} + +TEST(Test_BellmanFord, Test_4) { + WeightedGraph graph; + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + graph.AddVertex('D'); + graph.AddVertex('E'); + graph.AddVertex('F'); + graph.AddVertex('G'); + graph.AddVertex('H'); + + graph.AddWeightedEdge('A', 'B', 3); + graph.AddWeightedEdge('B', 'C', -2); + graph.AddWeightedEdge('A', 'C', 2); + graph.AddWeightedEdge('D', 'E', 5); + graph.AddWeightedEdge('D', 'F', 4); + graph.AddWeightedEdge('E', 'F', -4); + graph.AddWeightedEdge('E', 'G', -3); + graph.AddWeightedEdge('G', 'F', -2); + graph.AddWeightedEdge('F', 'G', 5); + + auto result = BellmanFord('A', graph); + std::vector> expected; + + expected.push_back(MinPath({'A', 'B'}, 3)); + expected.push_back(MinPath({'A', 'B', 'C'}, 1)); + expected.push_back(MinPath({'D'}, INF)); + expected.push_back(MinPath({'E'}, INF)); + expected.push_back(MinPath({'F'}, INF)); + expected.push_back(MinPath({'G'}, INF)); + expected.push_back(MinPath({'H'}, INF)); + + ASSERT_EQ(result, expected); +} + +TEST(Test_BellmanFord, Test_5) { + WeightedGraph graph; + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + graph.AddVertex('D'); + graph.AddVertex('E'); + graph.AddVertex('F'); + graph.AddVertex('G'); + graph.AddVertex('H'); + + graph.AddWeightedEdge('A', 'B', 3); + graph.AddWeightedEdge('B', 'C', -2); + graph.AddWeightedEdge('A', 'C', 2); + graph.AddWeightedEdge('D', 'E', 5); + graph.AddWeightedEdge('D', 'F', 4); + graph.AddWeightedEdge('E', 'F', -4); + graph.AddWeightedEdge('E', 'G', -3); + graph.AddWeightedEdge('G', 'F', -2); + graph.AddWeightedEdge('F', 'G', 5); + + auto result = BellmanFord('D', graph); + std::vector> expected; + + expected.push_back(MinPath({'A'}, INF)); + expected.push_back(MinPath({'B'}, INF)); + expected.push_back(MinPath({'C'}, INF)); + expected.push_back(MinPath({'D', 'E'}, 5)); + expected.push_back(MinPath({'D', 'E', 'G', 'F'}, 0)); + expected.push_back(MinPath({'D', 'E', 'G'}, 2)); + expected.push_back(MinPath({'H'}, INF)); -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] + ASSERT_EQ(result, expected); } + +TEST(Test_Johnson, Simple_Test_1) { + WeightedGraph graph; + + auto result = Johnson(graph); + std::map> expected; + + ASSERT_EQ(result, expected); +} + +TEST(Test_Johnson, Simple_Test_2) { + WeightedGraph graph; + + graph.AddVertex('A'); + + auto result = Johnson(graph); + std::map> expected; + + expected['A']['A'] = 0; + + ASSERT_EQ(result, expected); +} + +TEST(Test_Johnson, Simple_Test_3) { + WeightedGraph graph; + + graph.AddVertex('A'); + graph.AddVertex('B'); + + auto result = Johnson(graph); + std::map> expected; + + expected['A']['A'] = 0; + expected['A']['B'] = INF; + expected['B']['A'] = INF; + expected['B']['B'] = 0; + + ASSERT_EQ(result, expected); +} + +TEST(Test_Johnson, Simple_Test_4) { + WeightedGraph graph; + + graph.AddVertex('A'); + graph.AddVertex('B'); + + graph.AddWeightedEdge('A', 'B', -1); + graph.AddWeightedEdge('B', 'A', 2); + + auto result = Johnson(graph); + std::map> expected; + + expected['A']['A'] = 0; + expected['A']['B'] = -1; + expected['B']['A'] = 2; + expected['B']['B'] = 0; + + ASSERT_EQ(result, expected); +} + +TEST(Test_Johnson, Simple_Test_5) { + WeightedGraph graph; + + graph.AddVertex('A'); + graph.AddVertex('B'); + + graph.AddWeightedEdge('A', 'B', -2); + graph.AddWeightedEdge('B', 'A', 1); + + ASSERT_THROW(Johnson(graph), std::invalid_argument); +} + +TEST(Test_Johnson, Test_1) { + WeightedGraph graph; + + graph.AddVertex(0); + graph.AddVertex(1); + graph.AddVertex(2); + graph.AddVertex(3); + + graph.AddWeightedEdge(0, 1, 1); + graph.AddWeightedEdge(1, 2, 2); + graph.AddWeightedEdge(2, 3, 1); + graph.AddWeightedEdge(0, 2, 4); + + auto result = Johnson(graph); + std::map> expected; + + expected[0][0] = 0; + expected[0][1] = 1; + expected[0][2] = 3; + expected[0][3] = 4; + + expected[1][0] = INF; + expected[1][1] = 0; + expected[1][2] = 2; + expected[1][3] = 3; + + expected[2][0] = INF; + expected[2][1] = INF; + expected[2][2] = 0; + expected[2][3] = 1; + + expected[3][0] = INF; + expected[3][1] = INF; + expected[3][2] = INF; + expected[3][3] = 0; + + ASSERT_EQ(result, expected); +} + +TEST(Test_Johnson, Test_2) { + WeightedGraph graph; + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + graph.AddVertex('D'); + + graph.AddWeightedEdge('A', 'B', -2); + graph.AddWeightedEdge('A', 'D', 5); + graph.AddWeightedEdge('A', 'C', 7); + graph.AddWeightedEdge('B', 'C', 8); + graph.AddWeightedEdge('B', 'D', 6); + graph.AddWeightedEdge('C', 'B', 3); + graph.AddWeightedEdge('C', 'D', -4); + graph.AddWeightedEdge('D', 'A', -1); + + auto result = Johnson(graph); + std::map> expected; + + expected['A']['A'] = 0; + expected['A']['B'] = -2; + expected['A']['C'] = 6; + expected['A']['D'] = 2; + + expected['B']['A'] = 3; + expected['B']['B'] = 0; + expected['B']['C'] = 8; + expected['B']['D'] = 4; + + expected['C']['A'] = -5; + expected['C']['B'] = -7; + expected['C']['C'] = 0; + expected['C']['D'] = -4; + + expected['D']['A'] = -1; + expected['D']['B'] = -3; + expected['D']['C'] = 5; + expected['D']['D'] = 0; + + ASSERT_EQ(result, expected); +} + +TEST(Test_Johnson, Test_3) { + WeightedGraph graph; + + graph.AddVertex(1); + graph.AddVertex(2); + graph.AddVertex(3); + graph.AddVertex(4); + graph.AddVertex(5); + + graph.AddWeightedEdge(1, 2, -4); + graph.AddWeightedEdge(2, 3, 5); + graph.AddWeightedEdge(3, 1, 2); + graph.AddWeightedEdge(1, 4, 1); + graph.AddWeightedEdge(1, 5, -2); + graph.AddWeightedEdge(4, 5, 3); + + auto result = Johnson(graph); + std::map> expected; + + expected[1][1] = 0; + expected[1][2] = -4; + expected[1][3] = 1; + expected[1][4] = 1; + expected[1][5] = -2; + + expected[2][1] = 7; + expected[2][2] = 0; + expected[2][3] = 5; + expected[2][4] = 8; + expected[2][5] = 5; + + expected[3][1] = 2; + expected[3][2] = -2; + expected[3][3] = 0; + expected[3][4] = 3; + expected[3][5] = 0; + + expected[4][1] = INF; + expected[4][2] = INF; + expected[4][3] = INF; + expected[4][4] = 0; + expected[4][5] = 3; + + expected[5][1] = INF; + expected[5][2] = INF; + expected[5][3] = INF; + expected[5][4] = INF; + expected[5][5] = 0; + + ASSERT_EQ(result, expected); +} + +TEST(Test_Johnson, Test_4) { + WeightedGraph graph; + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + graph.AddVertex('D'); + graph.AddVertex('E'); + graph.AddVertex('F'); + graph.AddVertex('G'); + + graph.AddWeightedEdge('A', 'B', 5); + graph.AddWeightedEdge('A', 'C', 2); + graph.AddWeightedEdge('B', 'C', 1); + graph.AddWeightedEdge('B', 'D', 3); + graph.AddWeightedEdge('C', 'E', -2); + graph.AddWeightedEdge('C', 'F', 4); + graph.AddWeightedEdge('D', 'E', 2); + graph.AddWeightedEdge('D', 'G', 6); + graph.AddWeightedEdge('E', 'F', 3); + graph.AddWeightedEdge('E', 'G', -4); + graph.AddWeightedEdge('F', 'G', 1); + graph.AddWeightedEdge('F', 'C', -7); + graph.AddWeightedEdge('G', 'A', 8); + + ASSERT_THROW(Johnson(graph), std::invalid_argument); +} + +TEST(Test_Johnson, Test_5) { + WeightedGraph graph; + + graph.AddVertex("A"); + graph.AddVertex("B"); + graph.AddVertex("C"); + graph.AddVertex("D"); + graph.AddVertex("E"); + graph.AddVertex("F"); + + graph.AddWeightedEdge("A", "B", 2); + graph.AddWeightedEdge("A", "C", 1); + graph.AddWeightedEdge("A", "F", 8); + graph.AddWeightedEdge("B", "D", 4); + graph.AddWeightedEdge("B", "E", -3); + graph.AddWeightedEdge("C", "B", 5); + graph.AddWeightedEdge("C", "E", 5); + graph.AddWeightedEdge("C", "F", 6); + graph.AddWeightedEdge("D", "A", -1); + graph.AddWeightedEdge("D", "F", -3); + graph.AddWeightedEdge("E", "C", -2); + graph.AddWeightedEdge("E", "F", -2); + graph.AddWeightedEdge("E", "D", 3); + + auto result = Johnson(graph); + std::map> expected; + + expected["A"]["A"] = 0; + expected["A"]["B"] = 2; + expected["A"]["C"] = -3; + expected["A"]["D"] = 2; + expected["A"]["E"] = -1; + expected["A"]["F"] = -3; + + expected["B"]["A"] = -1; + expected["B"]["B"] = 0; + expected["B"]["C"] = -5; + expected["B"]["D"] = 0; + expected["B"]["E"] = -3; + expected["B"]["F"] = -5; + + expected["C"]["A"] = 4; + expected["C"]["B"] = 5; + expected["C"]["C"] = 0; + expected["C"]["D"] = 5; + expected["C"]["E"] = 2; + expected["C"]["F"] = 0; + + expected["D"]["A"] = -1; + expected["D"]["B"] = 1; + expected["D"]["C"] = -4; + expected["D"]["D"] = 0; + expected["D"]["E"] = -2; + expected["D"]["F"] = -4; + + expected["E"]["A"] = 2; + expected["E"]["B"] = 3; + expected["E"]["C"] = -2; + expected["E"]["D"] = 3; + expected["E"]["E"] = 0; + expected["E"]["F"] = -2; + + expected["F"]["A"] = INF; + expected["F"]["B"] = INF; + expected["F"]["C"] = INF; + expected["F"]["D"] = INF; + expected["F"]["E"] = INF; + expected["F"]["F"] = 0; + + ASSERT_EQ(result, expected); +} \ No newline at end of file diff --git a/task_03/src/topology_sort.cpp b/task_03/src/topology_sort.cpp deleted file mode 100644 index e53f670..0000000 --- a/task_03/src/topology_sort.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "topology_sort.hpp" diff --git a/task_03/src/topology_sort.hpp b/task_03/src/topology_sort.hpp deleted file mode 100644 index 6f70f09..0000000 --- a/task_03/src/topology_sort.hpp +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/task_04/CMakeLists.txt b/task_04/CMakeLists.txt index 7c1cb30..aeb7f47 100644 --- a/task_04/CMakeLists.txt +++ b/task_04/CMakeLists.txt @@ -27,6 +27,14 @@ include_directories(${GTEST_INCLUDE_DIRS}) find_library(Utils ../) target_link_libraries(${PROJECT_NAME} PUBLIC Utils) +file(GLOB_RECURSE lib_source_list "src/*.hpp") + +add_library(Dijkstra ${lib_source_list}) + +set_target_properties(Dijkstra PROPERTIES LINKER_LANGUAGE CXX) + +target_include_directories(Dijkstra PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src) + # Link runTests with what we want to test and the GTest and pthread library add_executable(${PROJECT_NAME}_tests ${test_source_list}) target_link_libraries( diff --git a/task_04/src/dijkstra.hpp b/task_04/src/dijkstra.hpp new file mode 100644 index 0000000..8fe7bc4 --- /dev/null +++ b/task_04/src/dijkstra.hpp @@ -0,0 +1,117 @@ +#pragma once + +#include +#include + +#include "graph/weighted_graph.hpp" + +#define INF std::numeric_limits::max() + +template +struct MinPath { + public: + MinPath() = default; + + MinPath(const std::vector& vertices, int weight) + : vertices(vertices), weight(weight) {} + + void PrintMinPath() { + for (int i = 0; i < vertices.size(); i++) { + std::cout << vertices[i]; + if (i != vertices.size() - 1) std::cout << "->"; + } + if (weight != INF) + std::cout << ": " << weight << '\n'; + else + std::cout << ": inf\n"; + } + + int weight; + std::vector vertices; +}; + +template +bool operator==(const MinPath& lhs, const MinPath& rhs) { + return lhs.vertices == rhs.vertices && lhs.weight == rhs.weight; +} + +template +void DijkstraStep(WeightedGraph& graph, + std::unordered_map>& min_paths, + std::unordered_map& visited) { + int min_weight = INF; + std::pair edge; + + for (auto [v, is_visited] : visited) { + if (is_visited) { + int weight_v = min_paths[v].weight; + for (auto u : graph.GetAdjVertices(v)) { + if (!visited[u]) { + int edge_weight = graph.GetEdgeWeight(v, u); + if (min_weight > weight_v + edge_weight) { + min_weight = weight_v + edge_weight; + edge.first = v; + edge.second = u; + } + } + } + } + } + + // Случай, когда какие-то вершины недостижимы + if (edge.first == T() && edge.second == T()) return; + + if (visited.size() && min_paths.size()) { + T v = edge.first, u = edge.second; // v - родитель, u - ребенок + visited[u] = true; + + // Добавляем минимальный найденный путь для вершины + for (auto v : min_paths[v].vertices) min_paths[u].vertices.push_back(v); + min_paths[u].vertices.push_back(v); + min_paths[u].weight = min_weight; + } + + for (auto [v, is_visited] : visited) + if (!is_visited) DijkstraStep(graph, min_paths, visited); +} + +template +std::vector> Dijkstra(const T& vertex, WeightedGraph graph) { + for (auto w_edge : graph.GetWeightedEdges()) + if (w_edge.weight < 0) + throw std::invalid_argument( + "Weighted graph has edges with negative weights!"); + + if (!graph.ContainsVertex(vertex)) + throw std::invalid_argument("Root vertex not found!"); + + std::unordered_map> min_paths; + std::unordered_map visited; + for (auto v : graph.GetVerticesIds()) { + if (v == vertex) + visited[v] = true; + else { + min_paths[v].weight = INF; + visited[v] = false; + } + } + + DijkstraStep(graph, min_paths, visited); + + // Удаляем вершину, для которой мы находили кратчайшие пути до других вершин + min_paths.erase(vertex); + + std::vector> paths; + for (auto [v, min_path] : min_paths) { + // Добавляем в путь конечную вершину + min_path.vertices.push_back(v); + + paths.push_back(min_path); + } + + // Переворачиваем массив, чтобы конечными точками в пути являлись первые + // добавленные вершины в графе + reverse(paths.begin(), paths.end()); + + return paths; +} \ No newline at end of file diff --git a/task_04/src/main.cpp b/task_04/src/main.cpp index 0e4393b..76e8197 100644 --- a/task_04/src/main.cpp +++ b/task_04/src/main.cpp @@ -1,3 +1 @@ -#include - int main() { return 0; } diff --git a/task_04/src/test.cpp b/task_04/src/test.cpp index 5e11617..abe49c0 100644 --- a/task_04/src/test.cpp +++ b/task_04/src/test.cpp @@ -1,6 +1,435 @@ - #include -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] +#include "dijkstra.hpp" + +TEST(Test_Dijkstra, Simple_Test_1) { + WeightedGraph graph; + + ASSERT_THROW(Dijkstra(4, graph), std::invalid_argument); +} + +TEST(Test_Dijkstra, Simple_Test_2) { + WeightedGraph graph; + + graph.AddVertex('A'); + + auto result = Dijkstra('A', graph); + std::vector> expected; + + ASSERT_EQ(result, expected); +} + +TEST(Test_Dijkstra, Simple_Test_3) { + WeightedGraph graph; + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + + graph.AddWeightedEdge('A', 'B', 1); + graph.AddWeightedEdge('A', 'C', 2); + graph.AddWeightedEdge('B', 'C', -3); + + ASSERT_THROW(Dijkstra('B', graph), std::invalid_argument); +} + +TEST(Test_Dijkstra, Simple_Test_4) { + WeightedGraph graph; + + graph.AddVertex('A'); + graph.AddVertex('B'); + + graph.AddWeightedEdge('A', 'B', 1); + + auto result = Dijkstra('A', graph); + std::vector> expected; + + expected.push_back(MinPath({'A', 'B'}, 1)); + + ASSERT_EQ(result, expected); +} + +TEST(Test_Dijkstra, Simple_Test_5) { + WeightedGraph graph; + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + + graph.AddWeightedEdge('A', 'B', 2); + graph.AddWeightedEdge('A', 'C', 6); + graph.AddWeightedEdge('B', 'C', 3); + + auto result = Dijkstra('A', graph); + std::vector> expected; + + expected.push_back(MinPath({'A', 'B'}, 2)); + expected.push_back(MinPath({'A', 'B', 'C'}, 5)); + + ASSERT_EQ(result, expected); +} + +TEST(Test_Dijkstra, Simple_Test_6) { + WeightedGraph graph; + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + + graph.AddWeightedEdge('A', 'B', 2); + graph.AddWeightedEdge('A', 'C', 6); + graph.AddWeightedEdge('B', 'C', 3); + + auto result = Dijkstra('B', graph); + std::vector> expected; + + expected.push_back(MinPath({'A'}, INF)); + expected.push_back(MinPath({'B', 'C'}, 3)); + + ASSERT_EQ(result, expected); +} + +TEST(Test_Dijkstra, Simple_Test_7) { + WeightedGraph graph(false); + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + + graph.AddWeightedEdge('A', 'B', 2); + graph.AddWeightedEdge('A', 'C', 6); + graph.AddWeightedEdge('B', 'C', 3); + + auto result = Dijkstra('B', graph); + std::vector> expected; + + expected.push_back(MinPath({'B', 'A'}, 2)); + expected.push_back(MinPath({'B', 'C'}, 3)); + + ASSERT_EQ(result, expected); +} + +TEST(Test_Dijkstra, Simple_Test_8) { + WeightedGraph graph; + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + + graph.AddWeightedEdge('A', 'B', 2); + graph.AddWeightedEdge('A', 'C', 6); + graph.AddWeightedEdge('B', 'C', 3); + + graph.AddVertex('D'); + graph.AddVertex('E'); + + graph.AddWeightedEdge('D', 'E', 4); + + auto result = Dijkstra('A', graph); + std::vector> expected; + + expected.push_back(MinPath({'A', 'B'}, 2)); + expected.push_back(MinPath({'A', 'B', 'C'}, 5)); + expected.push_back(MinPath({'D'}, INF)); + expected.push_back(MinPath({'E'}, INF)); + + ASSERT_EQ(result, expected); } + +TEST(Test_Dijkstra, Simple_Test_9) { + WeightedGraph graph(false); + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + + graph.AddWeightedEdge('A', 'B', 2); + graph.AddWeightedEdge('A', 'C', 6); + graph.AddWeightedEdge('B', 'C', 3); + + graph.AddVertex('D'); + graph.AddVertex('E'); + graph.AddVertex('F'); + + graph.AddWeightedEdge('D', 'E', 1); + graph.AddWeightedEdge('D', 'F', 9); + graph.AddWeightedEdge('F', 'E', 3); + + auto result = Dijkstra('B', graph); + std::vector> expected; + + expected.push_back(MinPath({'B', 'A'}, 2)); + expected.push_back(MinPath({'B', 'C'}, 3)); + expected.push_back(MinPath({'D'}, INF)); + expected.push_back(MinPath({'E'}, INF)); + expected.push_back(MinPath({'F'}, INF)); + + ASSERT_EQ(result, expected); +} + +TEST(Test_Dijkstra, Test_1) { + WeightedGraph graph(false); + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + graph.AddVertex('D'); + graph.AddVertex('E'); + graph.AddVertex('F'); + + graph.AddWeightedEdge('A', 'B', 6); + graph.AddWeightedEdge('A', 'C', 3); + graph.AddWeightedEdge('A', 'D', 2); + graph.AddWeightedEdge('B', 'C', 3); + graph.AddWeightedEdge('B', 'F', 4); + graph.AddWeightedEdge('C', 'E', 2); + graph.AddWeightedEdge('D', 'E', 5); + graph.AddWeightedEdge('F', 'E', 1); + + auto result = Dijkstra('A', graph); + std::vector> expected; + + expected.push_back(MinPath({'A', 'C', 'B'}, 6)); + expected.push_back(MinPath({'A', 'C'}, 3)); + expected.push_back(MinPath({'A', 'D'}, 2)); + expected.push_back(MinPath({'A', 'C', 'E'}, 5)); + expected.push_back(MinPath({'A', 'C', 'E', 'F'}, 6)); + + ASSERT_EQ(result, expected); +} + +TEST(Test_Dijkstra, Test_2) { + WeightedGraph graph(false); + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + graph.AddVertex('D'); + graph.AddVertex('E'); + graph.AddVertex('F'); + graph.AddVertex('G'); + graph.AddVertex('H'); + graph.AddVertex('I'); + + graph.AddWeightedEdge('E', 'B', 8); + graph.AddWeightedEdge('C', 'D', 3); + graph.AddWeightedEdge('E', 'G', 8); + graph.AddWeightedEdge('C', 'F', 1); + graph.AddWeightedEdge('E', 'D', 7); + graph.AddWeightedEdge('A', 'C', 12); + graph.AddWeightedEdge('G', 'H', 9); + graph.AddWeightedEdge('G', 'I', 2); + graph.AddWeightedEdge('H', 'I', 11); + graph.AddWeightedEdge('D', 'F', 3); + graph.AddWeightedEdge('F', 'H', 6); + graph.AddWeightedEdge('A', 'B', 10); + graph.AddWeightedEdge('E', 'H', 5); + graph.AddWeightedEdge('C', 'B', 9); + + auto result = Dijkstra('A', graph); + std::vector> expected; + + expected.push_back(MinPath({'A', 'B'}, 10)); + expected.push_back(MinPath({'A', 'C'}, 12)); + expected.push_back(MinPath({'A', 'C', 'D'}, 15)); + expected.push_back(MinPath({'A', 'B', 'E'}, 18)); + expected.push_back(MinPath({'A', 'C', 'F'}, 13)); + expected.push_back(MinPath({'A', 'B', 'E', 'G'}, 26)); + expected.push_back(MinPath({'A', 'C', 'F', 'H'}, 19)); + expected.push_back(MinPath({'A', 'B', 'E', 'G', 'I'}, 28)); + + ASSERT_EQ(result, expected); +} + +TEST(Test_Dijkstra, Test_3) { + WeightedGraph graph; + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + graph.AddVertex('D'); + graph.AddVertex('E'); + graph.AddVertex('F'); + graph.AddVertex('G'); + graph.AddVertex('H'); + graph.AddVertex('I'); + graph.AddVertex('J'); + + graph.AddWeightedEdge('A', 'B', 12); + graph.AddWeightedEdge('A', 'C', 7); + graph.AddWeightedEdge('A', 'D', 10); + graph.AddWeightedEdge('B', 'E', 6); + graph.AddWeightedEdge('B', 'F', 10); + graph.AddWeightedEdge('B', 'C', 9); + graph.AddWeightedEdge('C', 'E', 8); + graph.AddWeightedEdge('C', 'F', 13); + graph.AddWeightedEdge('C', 'G', 15); + graph.AddWeightedEdge('D', 'C', 2); + graph.AddWeightedEdge('D', 'G', 13); + graph.AddWeightedEdge('G', 'F', 5); + graph.AddWeightedEdge('G', 'I', 6); + graph.AddWeightedEdge('E', 'H', 14); + graph.AddWeightedEdge('F', 'E', 16); + graph.AddWeightedEdge('F', 'H', 4); + graph.AddWeightedEdge('F', 'I', 11); + graph.AddWeightedEdge('H', 'I', 7); + graph.AddWeightedEdge('H', 'J', 13); + graph.AddWeightedEdge('I', 'J', 8); + + auto result = Dijkstra('A', graph); + std::vector> expected; + + expected.push_back(MinPath({'A', 'B'}, 12)); + expected.push_back(MinPath({'A', 'C'}, 7)); + expected.push_back(MinPath({'A', 'D'}, 10)); + expected.push_back(MinPath({'A', 'C', 'E'}, 15)); + expected.push_back(MinPath({'A', 'C', 'F'}, 20)); + expected.push_back(MinPath({'A', 'C', 'G'}, 22)); + expected.push_back(MinPath({'A', 'C', 'F', 'H'}, 24)); + expected.push_back(MinPath({'A', 'C', 'G', 'I'}, 28)); + expected.push_back(MinPath({'A', 'C', 'G', 'I', 'J'}, 36)); + + ASSERT_EQ(result, expected); +} + +TEST(Test_Dijkstra, Test_4) { + WeightedGraph graph; + + graph.AddVertex(0); + graph.AddVertex(1); + graph.AddVertex(2); + graph.AddVertex(3); + graph.AddVertex(4); + graph.AddVertex(5); + graph.AddVertex(6); + graph.AddVertex(7); + graph.AddVertex(8); + graph.AddVertex(9); + + graph.AddWeightedEdge(0, 1, 7); + graph.AddWeightedEdge(0, 2, 2); + graph.AddWeightedEdge(0, 3, 5); + graph.AddWeightedEdge(1, 2, 3); + graph.AddWeightedEdge(1, 4, 6); + graph.AddWeightedEdge(2, 3, 1); + graph.AddWeightedEdge(2, 5, 8); + graph.AddWeightedEdge(3, 4, 4); + graph.AddWeightedEdge(3, 6, 9); + graph.AddWeightedEdge(4, 5, 2); + graph.AddWeightedEdge(4, 7, 1); + graph.AddWeightedEdge(5, 6, 7); + graph.AddWeightedEdge(5, 8, 3); + graph.AddWeightedEdge(6, 7, 5); + graph.AddWeightedEdge(6, 9, 4); + graph.AddWeightedEdge(7, 8, 6); + graph.AddWeightedEdge(8, 9, 10); + + auto result = Dijkstra(0, graph); + std::vector> expected; + + expected.push_back(MinPath({0, 1}, 7)); + expected.push_back(MinPath({0, 2}, 2)); + expected.push_back(MinPath({0, 2, 3}, 3)); + expected.push_back(MinPath({0, 2, 3, 4}, 7)); + expected.push_back(MinPath({0, 2, 3, 4, 5}, 9)); + expected.push_back(MinPath({0, 2, 3, 6}, 12)); + expected.push_back(MinPath({0, 2, 3, 4, 7}, 8)); + expected.push_back(MinPath({0, 2, 3, 4, 5, 8}, 12)); + expected.push_back(MinPath({0, 2, 3, 6, 9}, 16)); + + ASSERT_EQ(result, expected); +} + +TEST(Test_Dijkstra, Test_5) { + WeightedGraph graph; + + graph.AddVertex(0); + graph.AddVertex(1); + graph.AddVertex(2); + graph.AddVertex(3); + graph.AddVertex(4); + graph.AddVertex(5); + graph.AddVertex(6); + graph.AddVertex(7); + graph.AddVertex(8); + graph.AddVertex(9); + + graph.AddWeightedEdge(0, 1, 4); + graph.AddWeightedEdge(0, 2, 2); + graph.AddWeightedEdge(1, 3, 5); + graph.AddWeightedEdge(2, 3, 1); + graph.AddWeightedEdge(1, 2, 3); + graph.AddWeightedEdge(3, 0, 6); + graph.AddWeightedEdge(2, 0, 7); + graph.AddWeightedEdge(4, 5, 7); + graph.AddWeightedEdge(5, 6, 3); + graph.AddWeightedEdge(6, 7, 2); + graph.AddWeightedEdge(7, 8, 9); + graph.AddWeightedEdge(8, 9, 6); + graph.AddWeightedEdge(4, 6, 1); + graph.AddWeightedEdge(5, 8, 4); + graph.AddWeightedEdge(7, 5, 5); + graph.AddWeightedEdge(9, 4, 8); + + auto result = Dijkstra(0, graph); + std::vector> expected; + + expected.push_back(MinPath({0, 1}, 4)); + expected.push_back(MinPath({0, 2}, 2)); + expected.push_back(MinPath({0, 2, 3}, 3)); + expected.push_back(MinPath({4}, INF)); + expected.push_back(MinPath({5}, INF)); + expected.push_back(MinPath({6}, INF)); + expected.push_back(MinPath({7}, INF)); + expected.push_back(MinPath({8}, INF)); + expected.push_back(MinPath({9}, INF)); + + ASSERT_EQ(result, expected); +} + +TEST(Test_Dijkstra, Test_6) { + WeightedGraph graph; + + graph.AddVertex(0); + graph.AddVertex(1); + graph.AddVertex(2); + graph.AddVertex(3); + graph.AddVertex(4); + graph.AddVertex(5); + graph.AddVertex(6); + graph.AddVertex(7); + graph.AddVertex(8); + graph.AddVertex(9); + + graph.AddWeightedEdge(0, 1, 4); + graph.AddWeightedEdge(0, 2, 2); + graph.AddWeightedEdge(1, 3, 5); + graph.AddWeightedEdge(2, 3, 1); + graph.AddWeightedEdge(1, 2, 3); + graph.AddWeightedEdge(3, 0, 6); + graph.AddWeightedEdge(2, 0, 7); + graph.AddWeightedEdge(4, 5, 7); + graph.AddWeightedEdge(5, 6, 3); + graph.AddWeightedEdge(6, 7, 2); + graph.AddWeightedEdge(7, 8, 9); + graph.AddWeightedEdge(8, 9, 6); + graph.AddWeightedEdge(4, 6, 1); + graph.AddWeightedEdge(5, 8, 4); + graph.AddWeightedEdge(7, 5, 5); + graph.AddWeightedEdge(9, 4, 8); + + auto result = Dijkstra(4, graph); + std::vector> expected; + + expected.push_back(MinPath({0}, INF)); + expected.push_back(MinPath({1}, INF)); + expected.push_back(MinPath({2}, INF)); + expected.push_back(MinPath({3}, INF)); + expected.push_back(MinPath({4, 5}, 7)); + expected.push_back(MinPath({4, 6}, 1)); + expected.push_back(MinPath({4, 6, 7}, 3)); + expected.push_back(MinPath({4, 5, 8}, 11)); + expected.push_back(MinPath({4, 5, 8, 9}, 17)); + + ASSERT_EQ(result, expected); +} \ No newline at end of file diff --git a/task_05/CMakeLists.txt b/task_05/CMakeLists.txt index 7c1cb30..5002178 100644 --- a/task_05/CMakeLists.txt +++ b/task_05/CMakeLists.txt @@ -27,6 +27,14 @@ include_directories(${GTEST_INCLUDE_DIRS}) find_library(Utils ../) target_link_libraries(${PROJECT_NAME} PUBLIC Utils) +file(GLOB_RECURSE lib_source_list "src/*.hpp") + +add_library(RMQ ${lib_source_list}) + +set_target_properties(RMQ PROPERTIES LINKER_LANGUAGE CXX) + +target_include_directories(RMQ PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src) + # Link runTests with what we want to test and the GTest and pthread library add_executable(${PROJECT_NAME}_tests ${test_source_list}) target_link_libraries( diff --git a/task_05/src/RMQ.hpp b/task_05/src/RMQ.hpp new file mode 100644 index 0000000..f31d260 --- /dev/null +++ b/task_05/src/RMQ.hpp @@ -0,0 +1,127 @@ +#pragma once + +#include +#include +#include +#include + +#define INF std::numeric_limits::max() + +class SegmentMinTree { + public: + SegmentMinTree() = default; + + SegmentMinTree(std::vector data) : size(data.size()) { + int sz = pow(2, ceil(std::log2(data.size()))); + while (data.size() != sz) data.push_back(INF); + + tree.resize(2 * sz); + for (int i = 0; i < sz; i++) { + tree[i + sz] = data[i]; + } + + for (int i = 2 * sz - 1; i > 0; i -= 2) { + size_t r = i; + size_t l = i - 1; + tree[i / 2] = std::min(tree[l], tree[r]); + } + } + + void Set(int value, size_t pos) { + if (pos >= size) throw std::invalid_argument("Invalid value position!"); + + size_t tree_pos = pos + pow(2, log2(tree.size()) - 1); + tree[tree_pos] = value; + + for (int i = tree_pos / 2; i > 0; i /= 2) + tree[i] = std::min(tree[i * 2], tree[i * 2 + 1]); + } + + int FindSegmentMin(size_t a, size_t b) const { + if (!(a >= 0 && b < size && a <= b)) + throw std::invalid_argument("Invalid segment limits!"); + + return SegmentMin(a, b, 1, 0, tree.size() / 2 - 1); + } + + size_t FindSegmentMinPos(size_t a, size_t b) const { + if (!(a >= 0 && b < size && a <= b)) + throw std::invalid_argument("Invalid segment limits!"); + + return SegmentMinPos(a, b, 1, 0, tree.size() / 2 - 1); + } + + void PrintTree() const { + std::cout << "Tree: "; + for (int i = 0; i < tree.size(); i++) { + if (tree[i] != INF) + std::cout << tree[i] << ' '; + else + std::cout << "inf "; + } + std::cout << '\n'; + } + + size_t Size() const { return size; } + + private: + std::vector tree; + size_t size = 0; + + int SegmentMin(size_t a, size_t b, size_t min_pos, size_t l_min, + size_t r_min) const { + if (l_min > b || a > r_min) { + return INF; + } + if (a <= l_min && b >= r_min) + return tree[min_pos]; + else { + int mid = (l_min + r_min) / 2; + return std::min(SegmentMin(a, b, 2 * min_pos, l_min, mid), + SegmentMin(a, b, 2 * min_pos + 1, mid + 1, r_min)); + } + } + + // Отличается только тем, что возвращает позицию минимального элемента на + // отрезке + size_t SegmentMinPos(size_t a, size_t b, size_t min_pos, size_t l_min, + size_t r_min) const { + if (l_min > b || a > r_min) { + return INF; + } + if (a <= l_min && b >= r_min) { + return min_pos; + } else { + int mid = (l_min + r_min) / 2; + size_t pos_1 = SegmentMinPos(a, b, 2 * min_pos, l_min, mid); + size_t pos_2 = SegmentMinPos(a, b, 2 * min_pos + 1, mid + 1, r_min); + if (pos_1 == INF || pos_2 == INF) { + if (pos_1 == INF) + return pos_2; + else + return pos_1; + } else if (tree[pos_1] < tree[pos_2]) + return pos_1; + else + return pos_2; + } + } +}; + +class RMQ { + public: + RMQ() = default; + + RMQ(std::vector data) : tree(data) {} + + void Set(int value, size_t pos) { tree.Set(value, pos); } + + void SetData(std::vector data) { tree = SegmentMinTree(data); } + + size_t Size() const { return tree.Size(); } + + int operator()(int a, int b) const { return tree.FindSegmentMin(a, b); } + + private: + SegmentMinTree tree; +}; diff --git a/task_05/src/main.cpp b/task_05/src/main.cpp index 0e4393b..76e8197 100644 --- a/task_05/src/main.cpp +++ b/task_05/src/main.cpp @@ -1,3 +1 @@ -#include - int main() { return 0; } diff --git a/task_05/src/test.cpp b/task_05/src/test.cpp index 5e11617..4f0e9d6 100644 --- a/task_05/src/test.cpp +++ b/task_05/src/test.cpp @@ -1,6 +1,77 @@ - #include -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] +#include "RMQ.hpp" + +TEST(Test_RMQ, Simple_Test_1) { + RMQ rmq; + + ASSERT_THROW(rmq(0, 0), std::invalid_argument); +} + +TEST(Test_RMQ, Simple_Test_2) { + RMQ rmq({1}); + + ASSERT_EQ(rmq(0, 0), 1); +} + +TEST(Test_RMQ, Simple_Test_3) { + RMQ rmq({1, 2, 3}); + + ASSERT_EQ(rmq(0, 2), 1); +} + +TEST(Test_RMQ, Simple_Test_4) { + RMQ rmq({1, 2, 3}); + + ASSERT_EQ(rmq(1, 2), 2); +} + +TEST(Test_RMQ, Simple_Test_5) { + RMQ rmq({1, 2, 3}); + + ASSERT_THROW(rmq(1, 4), std::invalid_argument); } + +TEST(Test_RMQ, Test_1) { + RMQ rmq({7, 23, 1, 56, 14}); + + std::vector expected{7, 7, 1, 1, 1, 23, 1, 1, 1, 1, 1, 1, 56, 14, 14}; + + int count = 0; + for (int i = 0; i < rmq.Size(); i++) { + for (int j = i; j < rmq.Size(); j++) + ASSERT_EQ(rmq(i, j), expected[count++]); + } +} + +TEST(Test_RMQ, Test_2) { + RMQ rmq({15, 18, 87, 22, 39, 41, 13, 63, 93, 85, 54, 6, 48}); + + std::vector expected{15, 15, 15, 15, 15, 15, 13, 13, 13, 13, 13, 6, 6, + 18, 18, 18, 18, 18, 13, 13, 13, 13, 13, 6, 6, 87, + 22, 22, 22, 13, 13, 13, 13, 13, 6, 6, 22, 22, 22, + 13, 13, 13, 13, 13, 6, 6, 39, 39, 13, 13, 13, 13, + 13, 6, 6, 41, 13, 13, 13, 13, 13, 6, 6, 13, 13, + 13, 13, 13, 6, 6, 63, 63, 63, 54, 6, 6, 93, 85, + 54, 6, 6, 85, 54, 6, 6, 54, 6, 6, 6, 6, 48}; + + int count = 0; + for (int i = 0; i < rmq.Size(); i++) { + for (int j = i; j < rmq.Size(); j++) + ASSERT_EQ(rmq(i, j), expected[count++]); + } +} + +TEST(Test_RMQ, Test_3) { + RMQ rmq({1, 1, 2, 2, 2, 1, 1, 2, 4}); + + std::vector expected{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 2, 2, 2, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, + 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 4}; + + int count = 0; + for (int i = 0; i < rmq.Size(); i++) { + for (int j = i; j < rmq.Size(); j++) + ASSERT_EQ(rmq(i, j), expected[count++]); + } +} \ No newline at end of file diff --git a/task_06/CMakeLists.txt b/task_06/CMakeLists.txt index 7c1cb30..71a849f 100644 --- a/task_06/CMakeLists.txt +++ b/task_06/CMakeLists.txt @@ -27,12 +27,16 @@ include_directories(${GTEST_INCLUDE_DIRS}) find_library(Utils ../) target_link_libraries(${PROJECT_NAME} PUBLIC Utils) +find_library(RMQ ../) +target_link_libraries(${PROJECT_NAME} PUBLIC Utils RMQ) + # Link runTests with what we want to test and the GTest and pthread library add_executable(${PROJECT_NAME}_tests ${test_source_list}) target_link_libraries( ${PROJECT_NAME}_tests GTest::gtest_main Utils + RMQ ) include(GoogleTest) diff --git a/task_06/src/LCA.hpp b/task_06/src/LCA.hpp new file mode 100644 index 0000000..c68df9d --- /dev/null +++ b/task_06/src/LCA.hpp @@ -0,0 +1,108 @@ +#pragma once +#include +#include + +#include "RMQ.hpp" +#include "graph/graph.hpp" + +template +class LCA { + public: + LCA() = default; + + LCA(T root, Graph graph) { SetGraph(root, graph); } + + void SetGraph(T root, Graph new_graph) { + graph = new_graph; + + // Проверка свойства дерева + if (graph.GetVerticesCount() - 1 != graph.GetEdgesCount()) + throw std::invalid_argument("Graph is not tree!"); + + std::map vertices_depths; + std::unordered_map visited; + for (T v : graph.GetVerticesIds()) { + first_meet[v] = INF; + visited[v] = false; + } + + EulerianCycle(vertices_depths, visited, 0, root); + + std::vector rmq_data; + for (T v : cycle) rmq_data.push_back(vertices_depths[v]); + tree = SegmentMinTree(rmq_data); + + seg_graph_tree.resize(pow(2, ceil(log2(tree.Size()) + 1))); + + for (int i = 0; i < cycle.size(); i++) + seg_graph_tree[i + seg_graph_tree.size() / 2] = cycle[i]; + + for (int i = seg_graph_tree.size() - 1; i > 0; i -= 2) { + int right_depth = graph.ContainsVertex(seg_graph_tree[i]) + ? vertices_depths[seg_graph_tree[i]] + : INF; + int left_depth = graph.ContainsVertex(seg_graph_tree[i - 1]) + ? vertices_depths[seg_graph_tree[i - 1]] + : INF; + if (right_depth < left_depth) + seg_graph_tree[i / 2] = seg_graph_tree[i]; + else + seg_graph_tree[i / 2] = seg_graph_tree[i - 1]; + } + } + + T operator()(const T& vert_1, const T& vert_2) { + if (!graph.ContainsVertex(vert_1) || !graph.ContainsVertex(vert_2)) + throw std::invalid_argument("Node not found!"); + + size_t pos_1 = first_meet[vert_1]; + size_t pos_2 = first_meet[vert_2]; + + size_t min_pos = + tree.FindSegmentMinPos(std::min(pos_1, pos_2), std::max(pos_1, pos_2)); + + return seg_graph_tree[min_pos]; + } + + size_t NodesCount() const { return graph.GetVerticesCount(); } + + private: + Graph graph; + SegmentMinTree tree; + std::vector cycle; + // Дерево отрезков с использованием вершин + std::vector seg_graph_tree; + // Здесь находятся позиции первой встречи вершин в Эйлеровом цикле + std::map first_meet; + + void EulerianCycle(std::map& vertices_depths, + std::unordered_map& visited, int curr_depth, + T curr_vertex) { + // Проверка на цикл + if (visited[curr_vertex]) + throw std::invalid_argument("Tree must be acyclic!"); + visited[curr_vertex] = true; + + vertices_depths[curr_vertex] = curr_depth; + cycle.push_back(curr_vertex); + if (cycle.size() - 1 < first_meet[curr_vertex]) + first_meet[curr_vertex] = cycle.size() - 1; + + T parent = curr_vertex; + if (cycle.size() > 1) parent = cycle[cycle.size() - 2]; + + for (T child : graph.GetAdjVertices(curr_vertex)) { + if (parent != child) + EulerianCycle(vertices_depths, visited, + vertices_depths[curr_vertex] + 1, child); + } + + if (parent == curr_vertex) { + if (visited.size() != graph.GetVerticesCount()) + // Проверка на связности + throw std::invalid_argument("Tree must be connected!"); + return; + } else + cycle.push_back(parent); + } +}; \ No newline at end of file diff --git a/task_06/src/main.cpp b/task_06/src/main.cpp index 0e4393b..76e8197 100644 --- a/task_06/src/main.cpp +++ b/task_06/src/main.cpp @@ -1,3 +1 @@ -#include - int main() { return 0; } diff --git a/task_06/src/test.cpp b/task_06/src/test.cpp index 5e11617..94a7985 100644 --- a/task_06/src/test.cpp +++ b/task_06/src/test.cpp @@ -1,6 +1,197 @@ - #include -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] +#include "LCA.hpp" + +TEST(Test_LCA, Simple_Test_1) { + LCA lca; + + ASSERT_THROW(lca(1, 2), std::invalid_argument); +} + +TEST(Test_LCA, Simple_Test_2) { + Graph graph(false); + + graph.AddVertex(1); + graph.AddVertex(2); + + graph.AddEdge(1, 2); + + LCA lca(1, graph); + + ASSERT_EQ(lca(1, 2), 1); +} + +TEST(Test_LCA, Simple_Test_3) { + Graph graph(false); + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + + graph.AddEdge('A', 'B'); + graph.AddEdge('A', 'C'); + graph.AddEdge('B', 'C'); + + ASSERT_THROW((LCA('A', graph)), std::invalid_argument); +} + +TEST(Test_LCA, Simple_Test_4) { + Graph graph(false); + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + graph.AddVertex('D'); + graph.AddVertex('E'); + + graph.AddEdge('A', 'B'); + graph.AddEdge('A', 'C'); + graph.AddEdge('B', 'C'); + graph.AddEdge('D', 'E'); + + ASSERT_THROW((LCA('A', graph)), std::invalid_argument); +} + +TEST(Test_LCA, Simple_Test_5) { + Graph graph(false); + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + graph.AddVertex('D'); + graph.AddVertex('E'); + + graph.AddEdge('A', 'B'); + graph.AddEdge('A', 'C'); + graph.AddEdge('D', 'E'); + + ASSERT_THROW((LCA('A', graph)), std::invalid_argument); +} + +TEST(Test_LCA, Simple_Test_6) { + Graph graph(false); + + graph.AddVertex('A'); + graph.AddVertex('B'); + + ASSERT_THROW((LCA('A', graph)), std::invalid_argument); +} + +TEST(Test_LCA, Simple_Test_7) { + Graph graph(false); + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + + graph.AddEdge('A', 'B'); + graph.AddEdge('A', 'C'); + + LCA lca('A', graph); + + ASSERT_THROW(lca('A', 'D'), std::invalid_argument); + ASSERT_THROW(lca('D', 'A'), std::invalid_argument); +} + +TEST(Test_LCA, Test_1) { + Graph graph(false); + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + graph.AddVertex('D'); + graph.AddVertex('E'); + graph.AddVertex('F'); + + graph.AddEdge('A', 'B'); + graph.AddEdge('A', 'C'); + graph.AddEdge('B', 'D'); + graph.AddEdge('B', 'E'); + graph.AddEdge('C', 'F'); + + LCA lca('A', graph); + + std::vector expected{'A', 'A', 'A', 'A', 'A', 'A', 'A', 'B', 'A', + 'B', 'B', 'A', 'A', 'A', 'C', 'A', 'A', 'C', + 'A', 'B', 'A', 'D', 'B', 'A', 'A', 'B', 'A', + 'B', 'E', 'A', 'A', 'A', 'C', 'A', 'A', 'F'}; + + int count = 0; + for (auto v1 : graph.GetVerticesIds()) + for (auto v2 : graph.GetVerticesIds()) + ASSERT_EQ(lca(v1, v2), expected[count++]); } + +TEST(Test_LCA, Test_2) { + Graph graph(false); + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + graph.AddVertex('D'); + graph.AddVertex('E'); + graph.AddVertex('F'); + + graph.AddEdge('A', 'B'); + graph.AddEdge('B', 'C'); + graph.AddEdge('C', 'D'); + graph.AddEdge('D', 'E'); + graph.AddEdge('E', 'F'); + + LCA lca('A', graph); + + std::vector expected{'A', 'A', 'A', 'A', 'A', 'A', 'A', 'B', 'B', + 'B', 'B', 'B', 'A', 'B', 'C', 'C', 'C', 'C', + 'A', 'B', 'C', 'D', 'D', 'D', 'A', 'B', 'C', + 'D', 'E', 'E', 'A', 'B', 'C', 'D', 'E', 'F'}; + + int count = 0; + for (auto v1 : graph.GetVerticesIds()) + for (auto v2 : graph.GetVerticesIds()) + ASSERT_EQ(lca(v1, v2), expected[count++]); +} + +TEST(Test_LCA, Test_3) { + Graph graph(false); + + graph.AddVertex('A'); + graph.AddVertex('B'); + graph.AddVertex('C'); + graph.AddVertex('D'); + graph.AddVertex('E'); + graph.AddVertex('F'); + graph.AddVertex('G'); + graph.AddVertex('H'); + graph.AddVertex('I'); + graph.AddVertex('J'); + graph.AddVertex('K'); + + graph.AddEdge('A', 'B'); + graph.AddEdge('A', 'C'); + graph.AddEdge('B', 'D'); + graph.AddEdge('B', 'G'); + graph.AddEdge('B', 'E'); + graph.AddEdge('G', 'H'); + graph.AddEdge('G', 'I'); + graph.AddEdge('C', 'F'); + graph.AddEdge('F', 'J'); + graph.AddEdge('F', 'K'); + + LCA lca('A', graph); + + std::vector expected{ + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'B', 'A', + 'B', 'B', 'A', 'B', 'B', 'B', 'A', 'A', 'A', 'A', 'C', 'A', 'A', 'C', + 'A', 'A', 'A', 'C', 'C', 'A', 'B', 'A', 'D', 'B', 'A', 'B', 'B', 'B', + 'A', 'A', 'A', 'B', 'A', 'B', 'E', 'A', 'B', 'B', 'B', 'A', 'A', 'A', + 'A', 'C', 'A', 'A', 'F', 'A', 'A', 'A', 'F', 'F', 'A', 'B', 'A', 'B', + 'B', 'A', 'G', 'G', 'G', 'A', 'A', 'A', 'B', 'A', 'B', 'B', 'A', 'G', + 'H', 'G', 'A', 'A', 'A', 'B', 'A', 'B', 'B', 'A', 'G', 'G', 'I', 'A', + 'A', 'A', 'A', 'C', 'A', 'A', 'F', 'A', 'A', 'A', 'J', 'F', 'A', 'A', + 'C', 'A', 'A', 'F', 'A', 'A', 'A', 'F', 'K'}; + + int count = 0; + for (auto v1 : graph.GetVerticesIds()) + for (auto v2 : graph.GetVerticesIds()) + ASSERT_EQ(lca(v1, v2), expected[count++]); +} \ No newline at end of file